Почему в этом случае seekp () терпит неудачу? - PullRequest
1 голос
/ 02 августа 2020

Я использую эту программу, чтобы изменить каждую гласную в text.txt, чтобы она стала хэштегом ('#')

#include <fstream>
#include <iostream>

int main()
{
    std::fstream iofile{ "text.txt", std::ios::in | std::ios::out };

    char chChar;

    while (iofile.get(chChar))
    {
        switch (chChar)
        {
        case 'a':
        case 'e':
        case 'i':
        case 'o':
        case 'u':
        case 'A':
        case 'E':
        case 'I':
        case 'O':
        case 'U':

            iofile.seekp(-1, std::ios::cur);

            iofile << '#';

            iofile.seekg(iofile.tellg(), std::ios::beg); 
        }
    }
}

Итак, если исходное содержимое text.txt -

something is funny

Он превратится в

s#m#th#ng #s f#nny

Проблема в этой строке

iofile.seekg(iofile.tellg(), std::ios::beg); 

Эта строка сохраняет указатель файла в том же положении, поэтому я подумал, что могу изменить строку на эту

iofile.seekg(0, std::ios::cur);

Я думал, что это сделает то же самое, но этого не произошло. Когда я запускаю программу, консоль просто останавливается и не останавливается, а text.txt начинает печатать странные вещи. Но когда я заменяю его этой строкой, он снова работает

iofile.seekg(1, std::ios::cur);

Я подумал, что это должно переместить указатель файла слишком далеко (мы должны оставить его в этом положении, но здесь мы перемещаем его, передавая 1 букву). Однако это работает. Что происходит?

Ответы [ 2 ]

1 голос
/ 02 августа 2020

Хороший вопрос! Мне удалось воспроизвести вашу проблему с помощью Microsoft Visual Studio Community Edition 2019. Однако, используя Visual Studio Code и g ++ 9.3.0, я не столкнулся с той же проблемой. Похоже, это проблема компилятора, особенно с Microsoft Visual C ++.

Стандарт C ++ относится к стандартной библиотеке C относительно ограничений на файловые потоки:

Ограничения на чтение и запись последовательности, контролируемой объектом класса basic_filebuf такие же, как для чтения и записи с ФАЙЛАМИ стандартной библиотеки C.

В стандартной библиотеке C (пункт 6 7.19.5.3) говорится:

Когда файл открывается в режиме обновления («+» в качестве второго или третьего символа в приведенном выше списке значений аргументов режима), как ввод, так и вывод могут выполняться в связанном потоке. Однако за выводом не должен следовать непосредственно ввод без промежуточного вызова функции fflu sh или функции позиционирования файла (fseek, fsetpos или rewind), а ввод не должен сопровождаться напрямую выводом без промежуточный вызов функции позиционирования файла , если операция ввода не встречает endof-file.

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

iofile.seekg(0, std::ios::cur);

должна выполнить поиск (в ту же позицию относительно текущей позиции потока), но с MSVC ++, похоже, этого не происходит, и впоследствии следует неопределенное поведение. Однако, когда вы выполняете поиск с начала файла, задавая std::ios::beg, поиск фактически выполняется, и ваша программа работает должным образом.

1 голос
/ 02 августа 2020
iofile << '#';
iofile.flush();  // flush buffer when switching from output to input
iofile.seekg(0, std::ios::cur);

Я недавно прочитал книгу о iostreams, «Стандартные C ++ IOStreams и Lcocales». Книга описывает в разделе 1.4.3 «Двунаправленные файловые потоки: переключение с вывода на ввод»,

Когда вывод был записан в двунаправленные файловые потоки, попытка чтения сразу после записи в файловый поток приведет к «неопределенному результату». .... .... Операция чтения может завершиться неудачно без указания этого сбоя в любом случае;

Автор упомянул, что поток должен быть сброшен перед чтением после записи, и также написано, что вызов seekg (0, ios_base :: begin) имеет эффект опустошения внутреннего буфера, но я не уверен, работает ли seekg () с другим значением второго параметра, такого как ios_base :: cur.

...