Я столкнулся с очень странной проблемой, которую я могу создать только в 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
@RetiredNinja, у файла всего 4 супервайзера, у меня также есть проверка, чтобы убедиться, что я не превышаю 10 строк при чтении файла.
char buffer[MAX];, затем fgets (buffer, sizeof(buffer), supervisors), где Hecctor 89 больше 10 символов.





Ваш входной файл должен использовать окончания строк \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 На самом деле недостаток в реализации/конфигурации библиотеки. Файл был fopen()отредактирован в текстовом режиме ("r", а не "rb"), поэтому библиотека может преобразовывать окончания строк на лету. Конечно, проще не заморачиваться с этим, предполагая, что текстовый режим и двоичный режим идентичны, и указывать на всех, кто не использует одинокий \n для конца строки...
@DevSolar AFAIK преобразование \r\n в \n не совместимо с POSIX, поэтому даже если вы находитесь в лагере «позволяет конвертировать», это скорее стандартный недостаток, чем реализация библиотеки. ИМО, хотя POSIX поступил правильно, убрав различие между текстовым и двоичным режимами. Когда библиотека выполняет какие-либо преобразования, возникает сложность, заключающаяся в том, что fread может считывать число chars, отличное от указанного в размере файла (через ftell или другими способами), и это вызывает всевозможные ошибки, если программисты не знают об этом.
@DevSolar «библиотека может преобразовывать окончания строк на лету» -> Это должно относиться к собственным окончаниям строк. Текстовые файлы с альтернативными кодировками (BOM , EOL , Ctrl-Z) — это всегда проблема.
У меня вопрос, что произойдет, если файл был создан без окончания \r или \r\n? Пропускает ли [^\r\n] его только в том случае, если он не может найти окончания \r\n или это вызовет проблемы? Мне очень жаль, если такой вопрос глупый/вопрос "нуба".
@DanielBoos %[^\r\n] останавливает этот спецификатор, если читается один '\r' или '\n'. Он не прочитан для следующей части формата сканирования.
@YakovGalka Преобразование \r\n в \n соответствует стандарту C. И не имеет большого значения, что POSIX "удален" или нет, пока другие окончания строк все еще существуют - AFAIR Microsoft не питает особой любви к POSIX. ;-)
@DevSolar Реализация не должна различать текстовые потоки и двоичные потоки. В POSIX все потоки являются двоичными потоками, поэтому его стандартные вызовы библиотеки ввода-вывода не могут преобразовывать окончания строк.
@IanAbbott В этом нет необходимости, но вполне возможно и удобно, что делает недостатком отсутствие этого, и это все, что я сказал. Я не совсем понимаю, почему люди продолжают путать стандарт языка C и POSIX.
@DevSolar Вы предлагаете внести поправки в стандарт POSIX? Если да, то какие последовательности символов входного текстового потока должны быть преобразованы в новую строку? И во что должна быть преобразована новая строка в выходных текстовых потоках? Конечно, такое изменение невозможно для обратной совместимости с существующей кодовой базой.
@IanAbbott Почему у вас сложилось впечатление, что я каким-либо образом забочусь о POSIX? Прошлое, настоящее или будущее? Это Unix-аналог API Microsoft, непереносимое, специфичное для платформы дополнение к языку, который слишком много о себе думает.
@DevSolar Вы сказали, что это «недостаток в реализации библиотеки». «Библиотека» здесь — это glibc, которая не выполняет преобразование по замыслу из-за совместимости с POSIX. Отсюда следует, что вы либо говорите, что это недостаток POSIX, либо что glibc не должен был оставаться совместимым с POSIX, потому что вы не заботитесь о POSIX. В то время как мы могли бы конструктивно обсудить первое, последнее является просто полнейшей религиозной чушью. На самом деле никому, кроме разработчиков Windows, нет дела до прошлого, настоящего или будущего Windows. Лично я также использую двоичный ввод-вывод в Windows и записываю файлы с помощью LF.
@YakovGalka Только то, что Windows - не единственная платформа, которая НЕ использует один \n для окончания строки. Игнорирование других платформ является недостатком любой библиотеки, и сторонники POSIX столь же религиозно бессмысленны, заявляя, что они поняли это правильно, как и Windows. Это бессмысленно. Библиотека может переводить окончания строк в текстовом режиме, но не делает этого. Это недостаток. Период.
@DevSolar Даже если вас не волнует POSIX, я надеюсь, что вас волнует обратная совместимость.
@DevSolar На самом деле Windows - единственная оставшаяся современная ОС, которая все еще использует их. И даже в Windows многие инструменты без проблем понимают LF.
@YakovGalka Так это нормально, что glibc этого не делает? Я не согласен. Можем ли мы перестать спамить эту тему сейчас?
#define MAX 10слишком мал для обработки самой длинной строки в Builders.dat