2013-03-26 2 views
7

Рассмотрим эти два файла:fgetpos() зависит от символа новой строки

file1.txt (Windows перевод строки)

abc\r\n 
def\r\n 

file2.txt (Unix символ новой строки)

abc\n 
def\n 

I» ve заметил, что для файла file2.txt позиция, полученная с fgetpos, не увеличивается правильно. Я работаю над Windows.

Позвольте мне привести вам пример. Следующий код:

#include<cstdio> 

void read(FILE *file) 
{ 
    int c = fgetc(file); 
    printf("%c (%d)\n", (char)c, c); 

    fpos_t pos; 
    fgetpos(file, &pos); // save the position 
    c = fgetc(file); 
    printf("%c (%d)\n", (char)c, c); 

    fsetpos(file, &pos); // restore the position - should point to previous 
    c = fgetc(file);  // character, which is not the case for file2.txt 
    printf("%c (%d)\n", (char)c, c); 
    c = fgetc(file); 
    printf("%c (%d)\n", (char)c, c); 
} 

int main() 
{ 
    FILE *file = fopen("file1.txt", "r"); 
    printf("file1:\n"); 
    read(file); 
    fclose(file); 

    file = fopen("file2.txt", "r"); 
    printf("\n\nfile2:\n"); 
    read(file); 
    fclose(file); 

    return 0; 
} 

дает такой результат:

file1: 
a (97) 
b (98) 
b (98) 
c (99) 


file2: 
a (97) 
b (98) 
  (-1) 
  (-1) 

file1.txt работает, как и ожидалось, в то время как file2.txt ведет себя странно. Чтобы объяснить, что случилось с ним, я попытался следующий код:

void read(FILE *file) 
{ 
    int c; 
    fpos_t pos; 
    while (1) 
    { 
     fgetpos(file, &pos); 
     printf("pos: %d ", (int)pos); 
     c = fgetc(file); 
     if (c == EOF) break; 
     printf("c: %c (%d)\n", (char)c, c); 
    } 
} 

int main() 
{ 
    FILE *file = fopen("file1.txt", "r"); 
    printf("file1:\n"); 
    read(file); 
    fclose(file); 

    file = fopen("file2.txt", "r"); 
    printf("\n\nfile2:\n"); 
    read(file); 
    fclose(file); 

    return 0; 
} 

Я получил этот результат:

file1: 
pos: 0 c: a (97) 
pos: 1 c: b (98) 
pos: 2 c: c (99) 
pos: 3 c: 
(10) 
pos: 5 c: d (100) 
pos: 6 c: e (101) 
pos: 7 c: f (102) 
pos: 8 c: 
(10) 
pos: 10 

file2: 
pos: 0 c: a (97) // something is going wrong here... 
pos: -1 c: b (98) 
pos: 0 c: c (99) 
pos: 1 c: 
(10) 
pos: 3 c: d (100) 
pos: 4 c: e (101) 
pos: 5 c: f (102) 
pos: 6 c: 
(10) 
pos: 8 

Я знаю, что fpos_t не предназначена для интерпретации кодера, потому что это зависит от реализация. Однако приведенный выше пример объясняет проблемы с fgetpos/fsetpos.

Как возможно, что последовательность новой строки влияет на внутреннюю позицию файла, даже до того, как она встретит эти символы?

+0

Btw изменение «rt» на «rb» устраняет проблему, но это не решение для меня, потому что я должен прочитать некоторые числа и строки из файла, используя fscanf. – miloszmaki

+0

Я пробовал оба IDE: VS C++ 2010 и Dev-cpp. – miloszmaki

+1

Можете ли вы добавить еще одну строку текста в свои два файла. Я подозреваю, что он «исправит себя» в том смысле, что '\ r' не засчитывается как символ, но когда вы перейдете ко второй строке, позиция переместит один символ. Другими словами, '\ r \ n' учитывается как единое целое, которое« 1 символ большой ». Ваш второй пар не имеет смысла. Вы даже не достигли новой строки, и все же вы говорите, что все пошло не так - я подозреваю, что вы хотели показать что-то еще? –

ответ

3

Я бы сказал, что проблема, вероятно, вызвана тем, что второй файл запутывает реализацию, поскольку он открывается в текстовом режиме, но он не соответствует требованиям.

В стандарте,

Текстовый поток упорядоченная последовательность символов состоит в линий, каждая линия, состоящая из нуля или более символов плюс завершение новой строки символ

В вашем втором потоке файлов нет допустимых символов новой строки (так как он ищет \r\n для преобразования в символ новой строки внутри). В результате реализация может не понимать длину строки должным образом и безнадежно путаться, когда вы пытаетесь двигаться в ней.

Кроме того,

Символы могут должны быть добавлены, изменены или удалены на входе и выходе , чтобы соответствовать различным конвенциям для представления текста в принимающей среды.

Имейте в виду, что библиотека не просто читать каждый байт из файла, как вы называете fgetc - он будет читать весь файл (для одного настолько мал) в буфере потоков и работать над этим.

+0

Означает ли это, что 'fgetpos' /' fsetpos' не гарантированно корректно работает для файла с новыми символами Unix в Windows? – miloszmaki

+1

@miloszmaki - я понимаю, что в Windows текстовый режим требует текстовых файлов в стиле Windows для правильной работы. В стандарте указано, что текстовые файлы в других форматах могут быть изменены для использования в текстовом потоке. – teppic

+0

Итак, лучшим решением было бы заменить только символы '\ n' символами' \ r \ n' для файлов с различным форматированием? – miloszmaki

2

Я добавляю это как вспомогательную информацию для teppic's answer:

При работе с FILE*, который был открыт как текст вместо двоичного файла, тем fgetpos() функции в VC++ 11 (VS-2012), может (и делает для вашего file2.txt пример) в конечном итоге в этом участке коды:

// ... 

if (_osfile(fd) & FTEXT) { 
     /* (1) If we're not at eof, simply copy _bufsiz 
      onto rdcnt to get the # of untranslated 
      chars read. (2) If we're at eof, we must 
      look through the buffer expanding the '\n' 
      chars one at a time. */ 

     // ... 

     if (_lseeki64(fd, 0i64, SEEK_END) == filepos) { 

      max = stream->_base + rdcnt; 
      for (p = stream->_base; p < max; p++) 
       if (*p == '\n')      // <--- 
        /* adjust for '\r' */   // <--- 
        rdcnt++;      // <--- 

// ... 

Это предполагает, что любой \n символа в буфере был первоначально \r\n последовательности, которая была нормализована, когда данные были считаны в буфер. Поэтому есть моменты, когда он пытается учесть этот (теперь отсутствующий) символ \r, который, по его мнению, удалил предыдущую обработку файла из буфера. Эта конкретная настройка происходит, когда вы находитесь ближе к концу файла; однако существуют и другие аналогичные корректировки для учета удаленных \r байт в обработке fgetpos().

+0

Я не юрист, и это может попасть под честное использование, но исходный код для Microsoft CRT не является бесплатным для перераспределения (по крайней мере, в VS 2010, я предполагаю, что VS 2012 та же). Лицензия в нем - «Авторское право (c) Корпорация Microsoft. Все права защищены». –

+0

Я предполагаю, что этот небольшой фрагмент подпадает под справедливое использование. Если есть разногласия по этому поводу, я отлично справляюсь с удалением. –

Смежные вопросы