Fgets записывает разные строки из одного и того же файла в Linux и Windows - PullRequest
0 голосов
/ 11 ноября 2018

Я только что натолкнулся на проблему, когда я переключался между valgrind в Linux и другим тестированием в Windows cmd.

Я читаю определенную строку из файла, подобного этому:

fgets(buf, MAX_LINE_LEN, f_input);

Конечно, buf - это размер MAX_LINE_LEN + 1, но я отвлекся.

Это вывод

printf("String length: %u; Contents: ", strlen(buf));
for (usint i = 0; i < strlen(buf); i++)
  printf("%x ", buf[i]);
puts(";");

в Windows:

String length: 14; Contents: 41 6e 64 72 65 6a 20 50 6c 61 76 6b 61 a ;
String length: 22; Contents: 41 6e 6e 61 20 4d 61 72 69 61 20 43 69 63 6d 61 6e 63 6f 76 61 a ;
String length: 25; Contents: 4d 61 72 69 61 20 52 61 7a 75 73 6f 76 61 20 4d 61 72 74 61 6b 6f 76 61 a ;
String length: 24; Contents: 4d 69 6c 61 6e 20 52 61 73 74 69 73 6c 61 76 20 50 6f 6b 6f 6a 6e 79 a ;
String length: 21; Contents: 4d 69 6c 65 6e 61 20 53 65 64 6d 69 6b 72 61 73 6b 6f 76 61 a ;
String length: 15; Contents: 56 69 6e 63 65 6e 74 20 53 69 6b 75 6c 61 a ;
String length: 17; Contents: 56 69 6e 63 65 6e 74 20 76 61 6e 20 47 6f 67 68 a ;

и в Linux:

String length: 15; Contents: 41 6e 64 72 65 6a 20 50 6c 61 76 6b 61 d a ;
String length: 23; Contents: 41 6e 6e 61 20 4d 61 72 69 61 20 43 69 63 6d 61 6e 63 6f 76 61 d a ;
String length: 26; Contents: 4d 61 72 69 61 20 52 61 7a 75 73 6f 76 61 20 4d 61 72 74 61 6b 6f 76 61 d a ;
String length: 25; Contents: 4d 69 6c 61 6e 20 52 61 73 74 69 73 6c 61 76 20 50 6f 6b 6f 6a 6e 79 d a ;
String length: 22; Contents: 4d 69 6c 65 6e 61 20 53 65 64 6d 69 6b 72 61 73 6b 6f 76 61 d a ;
String length: 16; Contents: 56 69 6e 63 65 6e 74 20 53 69 6b 75 6c 61 d a ;
String length: 18; Contents: 56 69 6e 63 65 6e 74 20 76 61 6e 20 47 6f 67 68 d a ;

Как вы можете видеть в Linux, перед NL есть еще один символ - возврат каретки. Если кто-то может объяснить это и избавить меня от необходимости добавлять операторы ifdef для кода Linux и Windows, я был бы признателен за это. Я понимаю, что linux добавляет возврат каретки после каждой строки, но действительно ли это предполагаемое поведение, когда оно затем читается fgets?

Ответы [ 3 ]

0 голосов
/ 11 ноября 2018

Как вы можете видеть в Linux, перед NL есть еще один символ - возврат каретки.

Это потому, что ваши файлы используют символы новой строки CR + LF, то есть каждая новая строка на самом деле состоит из двух символов: "\r\n".

Если вы открываете файлы без флага "b" в Windows, его библиотека C будет конвертировать каждый \n, который вы пишете, в \r\n, а каждый \r\n, который вы читаете, в \n.

Используйте флаг "b" fopen () в Windows, чтобы увидеть фактическое содержимое файла.

Когда вы читаете строку, используя fgets(buf, sizeof buf, handle), вы можете использовать buf[strcspn(buf, "\r\n")] = '\0'; для удаления новой строки.

0 голосов
/ 11 ноября 2018

В C вы открываете поток файлов в режиме text или binary . В двоичном режиме перевод не происходит, а входные и выходные данные являются байтами в файле. В текстовом режиме символ «новой строки» C переводится в то, что является общим на рассматриваемой платформе. В одной UNIX-подобной системе это 0A байт, а в DOS-подобных системах это 0D байт, за которым следует 0A байт. Есть другие случаи на других операционных системах, перечисленных здесь:

https://en.wikipedia.org/wiki/Newline

Чтобы вам не приходилось справляться с каждым различным текстовым форматом в каждой программе, все они переводятся в символ \n, насколько программа C видит в случае по умолчанию (текстовый режим). Слой ввода / вывода выполняет необходимые переводы для вас.

Когда вы используете fopen(), чтобы открыть поток файлов в C для чтения или записи, вы предоставляете параметр «файловый режим» - вы, вероятно, используете его здесь как "r" для чтения файл или "w", чтобы написать один. Если вы хотите выполнить перевод новой строки, вы можете указать, что поток открывается в режиме binary , с "rb" для чтения или "wb" для записи.

0 голосов
/ 11 ноября 2018

MS и Linux по-разному ожидают окончания строки текстового файла: "\r\n" против "\n".

Чтобы справиться, рекомендуется после fgets() использовать strcspn(), чтобы обрезать потенциальный конец последовательности строк, будь то "\n", "\r\n" или отсутствует.

fgets(buf, MAX_LINE_LEN, f_input);
buf[strcspn(buf, "\n\r")] = '\0';

Некоторые компиляторы в Windows будут использовать "\n" в качестве последовательности конца строки, а другие используют "\r\n". Поэтому я приписываю это изменение компиляторам и их производителям больше, чем ОС. Также некоторые старые текстовые MAC-файлы заканчиваются на '\r' и в Linux будут содержать fgets().

Далее: чтение файла с "\r\n" в виде текстового файла , который ожидает "\n" в качестве последовательности конца строки, имеет проблему при чтении полного буфера как "......\r" и остаток строки как "\n" на следующей fgets(). Для обработки требуется дополнительная обработка, как в случае, когда буфера недостаточно для строки ввода.

Текстовые файлы одного варианта часто копируются на другие платформы, так что это не редкость.

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

Педантичный код будет считывать файл в двоичном виде и обрабатывать окончание строки варианта без fgets(). Удачи.

...