Добавление запятой между кавычками (формат) в printf меняет вывод?

Я столкнулся с очень странной проблемой, которую я могу создать только в Linux. Я действительно не мог найти ничего подобного в Интернете, но если мои навыки поиска недостаточно хороши, я извиняюсь, если это было повторением какого-либо вопроса.

Возьмем следующий минимально жизнеспособный код ниже: Я урезал все, что мог, но все еще имел проблему.

#include <stdio.h>

#define MAX 10
#define MAXNAME 30

typedef struct sup{
    int rating;
    char supName[MAXNAME];
}sup;

typedef struct spec{
    char name[MAXNAME];
    float width;
    float height;
    float depth;
    char material[MAXNAME];
    sup supvisor[MAX];
}spec;

int read_Supervisors(char fileName[], spec *specread){
    int i=0;

    char buffer[MAX];
    FILE *supervisors = fopen(fileName, "r");

    while (i < MAX && fgets (buffer, sizeof(buffer), supervisors)) {

        if (sscanf(buffer, "%19s %d", specread->supvisor[i].supName, &specread->supvisor[i].rating) == 2){
            i++;}}

    fclose(supervisors);

    return i;
}

void read_Specs(char fileName[],spec *spec1){
    FILE *specs = fopen(fileName, "r");

    fscanf(specs, "%29[^\n]\n %19s\n %f\n %f\n %f", spec1->name, spec1->material,&spec1->width, &spec1->height, &spec1->depth);
    fclose(specs);
}

int main(int argc, char *argv[]) {
    spec spec1 = {.name = ""}; // Initialize .name at zero
    //Read specs file.
    read_Specs(argv[1],&spec1);

    int iteration = read_Supervisors(argv[2], &spec1), 
    selectedSuper = 1, 
    calculatedDays = 50;

    float priceOfProject = 1;

    //Print all the values.
    printf("Dear %s,"
           "\nThank you for your order.\n"
           "Your retaining wall of specifications:\n"
           "Material: %s\n"
           "width: %.2f m\n"
           "height: %.2f m\n"
           "depth: %.3f m\n"
           "Will be completed in %d days and the assigned project supervisor is %s\n"
           "The estimated price is: $%.2f\n", spec1.name, spec1.material, spec1.width,spec1.height,spec1.depth,calculatedDays, spec1.supvisor[selectedSuper].supName,priceOfProject);
    return 0;
}

Выход:

,ear Jason Oliver
Thank you for your order.
Your retaining wall of specifications:
Material: Concrete
width: 5.50 m
height: 1.00 m
depth: 0.250 m
Will be completed in 50 days and the assigned project supervisor is Sally
The estimated price is: $1.00

Обратите внимание, что на распечатке было ,ухо, а не Дорогой Джейсон Оливер, при удалении запятой Dear %s, до Dear %s работает отлично. Я могу поставить запятую перед %s, но не после.

Я пробовал много вещей, увеличивая #define, я пытался разделить на несколько printf, я пробовал разные версии C11, C23, я также пытался обновить свой Linux. Отмечу, что это чистая установка Linux.

Вот некоторая информация о среде linux (22.04), которую я использую: Linux Linux 5.19.0-35-generiC#36~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Пт, 17 февраля, 15:17:25 UTC 2 x86_64 x86_64 x86_64 GNU/Linux

Версия компилятора: gcc (Ubuntu 11.3.0-1ubuntu1~22.04) 11.3.0

структура файлов:

файл спецификаций.dat:

Jason Oliver
Concrete
5.50
1.0
0.250

Файл Builders.dat:

Bob 50
Sally 78
Jian 69
Hecctor 89
#define MAX 10 слишком мал для обработки самой длинной строки в Builders.dat
Retired Ninja 21.04.2023 16:40

@RetiredNinja, у файла всего 4 супервайзера, у меня также есть проверка, чтобы убедиться, что я не превышаю 10 строк при чтении файла.

Daniel Boos 21.04.2023 16:42
char buffer[MAX];, затем fgets (buffer, sizeof(buffer), supervisors), где Hecctor 89 больше 10 символов.
Retired Ninja 21.04.2023 16:44
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
5
3
110
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Ваш входной файл должен использовать окончания строк \r\n, распространенные в Windows, а не одиночные \n, которые использует Unix. Ваш код считывает имя до символа \n, поэтому \r становится частью name. Когда вы печатаете имя на терминале, \r указывает ему переместить курсор в начало строки (это управляющий символ возврата каретки). Следующим символом, который вы печатаете после этого, является запятая, поэтому она перезаписывает букву «D» слова «Dear», которая находится в той же позиции.

Вы также можете исправить свой fscanf, чтобы исключить \r из строки. Обратите внимание, что следующая \n в строке формата в такой ситуации неверна. Простой пробел поможет пропустить как \r, так и \n, а также другие пробельные символы:

fscanf(specs, "%29[^\r\n] %19s %f %f %f", spec1->name, spec1->material,&spec1->width, &spec1->height, &spec1->depth);

В качестве альтернативы, если это возможно, вы можете исправить свои входные файлы с помощью команды dos2unix, которая доступна для большинства операционных систем:

$ dos2unix specs.dat

Хороший улов. Жаль, что нас до сих пор мучает эта глупая проблема '\n' vs. '\r\n'.

Jabberwocky 21.04.2023 16:50

@Jabberwocky На самом деле недостаток в реализации/конфигурации библиотеки. Файл был fopen()отредактирован в текстовом режиме ("r", а не "rb"), поэтому библиотека может преобразовывать окончания строк на лету. Конечно, проще не заморачиваться с этим, предполагая, что текстовый режим и двоичный режим идентичны, и указывать на всех, кто не использует одинокий \n для конца строки...

DevSolar 21.04.2023 16:54

@DevSolar AFAIK преобразование \r\n в \n не совместимо с POSIX, поэтому даже если вы находитесь в лагере «позволяет конвертировать», это скорее стандартный недостаток, чем реализация библиотеки. ИМО, хотя POSIX поступил правильно, убрав различие между текстовым и двоичным режимами. Когда библиотека выполняет какие-либо преобразования, возникает сложность, заключающаяся в том, что fread может считывать число chars, отличное от указанного в размере файла (через ftell или другими способами), и это вызывает всевозможные ошибки, если программисты не знают об этом.

Yakov Galka 21.04.2023 16:59

@DevSolar «библиотека может преобразовывать окончания строк на лету» -> Это должно относиться к собственным окончаниям строк. Текстовые файлы с альтернативными кодировками (BOM , EOL , Ctrl-Z) — это всегда проблема.

chux - Reinstate Monica 21.04.2023 17:01

У меня вопрос, что произойдет, если файл был создан без окончания \r или \r\n? Пропускает ли [^\r\n] его только в том случае, если он не может найти окончания \r\n или это вызовет проблемы? Мне очень жаль, если такой вопрос глупый/вопрос "нуба".

Daniel Boos 21.04.2023 17:17

@DanielBoos %[^\r\n] останавливает этот спецификатор, если читается один '\r' или '\n'. Он не прочитан для следующей части формата сканирования.

chux - Reinstate Monica 21.04.2023 17:23

@YakovGalka Преобразование \r\n в \n соответствует стандарту C. И не имеет большого значения, что POSIX "удален" или нет, пока другие окончания строк все еще существуют - AFAIR Microsoft не питает особой любви к POSIX. ;-)

DevSolar 21.04.2023 17:33

@DevSolar Реализация не должна различать текстовые потоки и двоичные потоки. В POSIX все потоки являются двоичными потоками, поэтому его стандартные вызовы библиотеки ввода-вывода не могут преобразовывать окончания строк.

Ian Abbott 21.04.2023 18:50

@IanAbbott В этом нет необходимости, но вполне возможно и удобно, что делает недостатком отсутствие этого, и это все, что я сказал. Я не совсем понимаю, почему люди продолжают путать стандарт языка C и POSIX.

DevSolar 22.04.2023 14:33

@DevSolar Вы предлагаете внести поправки в стандарт POSIX? Если да, то какие последовательности символов входного текстового потока должны быть преобразованы в новую строку? И во что должна быть преобразована новая строка в выходных текстовых потоках? Конечно, такое изменение невозможно для обратной совместимости с существующей кодовой базой.

Ian Abbott 22.04.2023 17:58

@IanAbbott Почему у вас сложилось впечатление, что я каким-либо образом забочусь о POSIX? Прошлое, настоящее или будущее? Это Unix-аналог API Microsoft, непереносимое, специфичное для платформы дополнение к языку, который слишком много о себе думает.

DevSolar 22.04.2023 23:30

@DevSolar Вы сказали, что это «недостаток в реализации библиотеки». «Библиотека» здесь — это glibc, которая не выполняет преобразование по замыслу из-за совместимости с POSIX. Отсюда следует, что вы либо говорите, что это недостаток POSIX, либо что glibc не должен был оставаться совместимым с POSIX, потому что вы не заботитесь о POSIX. В то время как мы могли бы конструктивно обсудить первое, последнее является просто полнейшей религиозной чушью. На самом деле никому, кроме разработчиков Windows, нет дела до прошлого, настоящего или будущего Windows. Лично я также использую двоичный ввод-вывод в Windows и записываю файлы с помощью LF.

Yakov Galka 23.04.2023 00:08

@YakovGalka Только то, что Windows - не единственная платформа, которая НЕ использует один \n для окончания строки. Игнорирование других платформ является недостатком любой библиотеки, и сторонники POSIX столь же религиозно бессмысленны, заявляя, что они поняли это правильно, как и Windows. Это бессмысленно. Библиотека может переводить окончания строк в текстовом режиме, но не делает этого. Это недостаток. Период.

DevSolar 23.04.2023 12:46

@DevSolar Даже если вас не волнует POSIX, я надеюсь, что вас волнует обратная совместимость.

Ian Abbott 23.04.2023 16:16

@DevSolar На самом деле Windows - единственная оставшаяся современная ОС, которая все еще использует их. И даже в Windows многие инструменты без проблем понимают LF.

Yakov Galka 23.04.2023 17:05

@YakovGalka Так это нормально, что glibc этого не делает? Я не согласен. Можем ли мы перестать спамить эту тему сейчас?

DevSolar 23.04.2023 19:12

Другие вопросы по теме