Почему переход R/W в середине файла в режиме rb+ не работает, если я не использую fseek(fp,0,SEEK_CUR)?Почему он работает в конце файла?

Я никогда не осознавал этого. Я мог бы очень хорошо предположить подсознательно, как непреложный факт, что я могу переходить между чтением и записью в существующем файле, открывая его в режиме обновления, просто так. Но два вопроса по SO (1,2) заставил меня скептически, и я решил попробовать. Вот что я нахожу:

В первой программе, prog1, я работаю с файлом source.txt, в котором есть только строка Смертельное оружие. Я читаю Смертельное оружие из первого мира, используя fscanf(), и намереваюсь написать "мушкет" сразу после этого, ожидая получить Смертельный мушкет. Но он просто терпит неудачу, и я все еще получаю исходное содержание Смертельное оружие в конце записи операция. Но в этой первой программе, если я вставляю строку fseek(fp,0,SEEK_CUR), операция записи работает нормально, и я получаю Смертельный мушкет. Я заметил, что fseek(fp,0,SEEK_CUR) не служит никакой другой цели, кроме вызова fseek() просто ради этого , так как нет поиска по сети вообще.

Но во второй программе, prog2, тот же самый сценарий не нуждается в этом оператореfseek(fp,0,SEEK_CUR). Точнее, во второй программе, в отличие от чтения до середины файла, как в первой программе, здесь я читаю до конца файла, и затем начните писать туда. Здесь запись прошла успешно даже без использования fseek(fp,0,SEEK_CUR) и я получаю нужный контент Продажа летального оружия.

Вопрос: Почему мы не можем перейти из режима read в режим write в середине файла и какая разница в том, что fseek(fp,0,SEEK_CUR) заработает? И в то же время, почему тот же самый переход работает без суеты если мы прочитаем до конца файла и запишем туда? Почему fseek(fp,0,SEEK_CUR) не нужен во втором случае? Насколько целесообразно использование fseek(fp,0,SEEK_CUR) для успешной записи в первой программе? Есть ли лучшая альтернатива?

Два вопроса о SO, похоже, в некоторой степени касались одной и той же проблемы, но, поскольку они были основаны больше на поиске объяснения выдержек из текстов/книг, ответы, ориентированные на них, похоже, не касаются того, что я намереваюсь знать в точных терминах .

//PROG1

#include <stdio.h>

int main ()
{
    FILE *fp=fopen("D:\\source.txt","r+");
    char arr[20],brr[50];
    fscanf(fp,"%s",arr);
    printf("String is %s\n",arr);
    //fseek(fp,0,SEEK_CUR);  //Doesn't work without it
    fprintf(fp," musket");
    rewind(fp);
    fgets(brr,50,fp);
    printf("New string is %s",brr);
    fclose(fp);
}

Вывод:

1) Без fseek() -- Смертельное оружие

2) С fseek() -- Смертельный мушкет

//PROG2    

#include <stdio.h>

int main ()
{
    FILE *fp=fopen("D:\\source.txt","r+");
    char arr[20],brr[50];
    fgets(arr,20,fp);
    printf("Initial line is %s\n",arr);
    fprintf(fp," sale"); //writes to end of file
    rewind(fp);
    printf("New string is %s",fgets(brr,50,fp));
    fclose(fp);
}

Вывод без fseek(): -- Lethal weapon sale


person Rüppell's Vulture    schedule 24.05.2013    source источник
comment
режим «b» - это двоичный режим, обычно используется fread для чтения и fwrite для записи. И я пробую ваш код, но поведение отличается   -  person MYMNeo    schedule 24.05.2013
comment
@MYMNeo Поскольку я не использую числа, двоичный режим здесь не проблема. Тем не менее, я согласен, что лучше использовать текстовый режим, чтобы проиллюстрировать мою точку зрения. Редактирую его. Спасибо за указание. Кстати, как ведет себя ваш компьютер?   -  person Rüppell's Vulture    schedule 24.05.2013
comment
@MYMNeo И я должен отметить, что fread не очень помогает при чтении строк из файлов, поскольку вам нужно самостоятельно добавлять нулевой символ, в отличие от fscanf()   -  person Rüppell's Vulture    schedule 24.05.2013
comment
PROG1, № fseek тоже мушкет Смертельный   -  person MYMNeo    schedule 24.05.2013
comment
@MYMNeo Я дважды проверил это. Это все еще смертоносное оружие без fseek() на моем компьютере, и, кажется, оно согласуется с тем, что говорят два других вопроса и их ссылки.   -  person Rüppell's Vulture    schedule 24.05.2013
comment
Я нахожу строки в расширенном программировании в среде UNIX. В нем говорится, что при чтении и записи одного и того же файла с типом +, если нет fflush, fseek, fsetpos или rewind, невозможно записать содержимое после вывода. (Мой родной язык не английский, это мой перевод не происхождение).   -  person MYMNeo    schedule 24.05.2013
comment
@MYMNeo Кажется, мы оба заблудились, MYMNeo. Давайте подождем и посмотрим, что ответят более умные ребята. Будем надеяться, что я получу ответы до того, как назначу за это награду.   -  person Rüppell's Vulture    schedule 24.05.2013
comment
Почему второму не нужен fseek(), я думаю, что ключом является последний бит того, что вы указали ниже: операция чтения, которая не достигла конца файла, за которой следует операция записи . Во втором случае прочитанный DID достигает конца файла, поэтому сброс/fseek не требуется. Что касается дисперсии в первом примере, то она будет зависеть от реализации: некоторые компиляторы/платформы/ОС будут требовать fseek/flush, но на некоторых они могут работать без (но , так как он не определен для работы, вы все равно должны его включить).   -  person TripeHound    schedule 19.03.2014
comment
Я всегда представлял эту ситуацию как наличие указателя READ и указателя WRITE. Вы читаете первое слово, но указатель WRITE все еще «не определен» (в некотором смысле). fseek устанавливает указатели READ & WRITE, позволяя записи работать как положено. Возможно, это немного странное воображение, но оно работает на меня уже много лет. Так же, если читать весь файл (как в проге2, обновляется указатель WRITE), то можно нормально писать. Опять же, это почти «магия», но это работает для меня. Два указателя, но они не отслеживают друг друга автоматически.   -  person lornix    schedule 04.06.2014


Ответы (1)


На самом деле это похоже на ошибку в вашей реализации libc. Потоки файлового ввода-вывода обычно представляют собой абстракцию libc над двоичным вводом-выводом на основе дескриптора файла, реализованным ядром ОС. Таким образом, любое странное поведение должно быть связано с вашими специфическими причудами libc.

Поскольку вы, очевидно, используете Windows, это может быть источником ваших проблем. Какой компилятор вы используете? С GCC 4.6.1 и glibc-2.13 в Ubuntu 11.10 такой проблемы нет.

person Alexander Amelkin    schedule 24.05.2013
comment
Я использую CodeBlocks/gcc в Windows. - person Rüppell's Vulture; 24.05.2013
comment
И цитирую cplusplusreference "For files open for update (those which include a "+" sign), on which both input and output operations are allowed, the stream should be flushed (fflush) or repositioned (fseek, fsetpos, rewind) between either a writing operation followed by a reading operation or a reading operation which did not reach the end-of-file followed by a writing operation." - person Rüppell's Vulture; 24.05.2013