Почему начало моей строки исчезает? - PullRequest
6 голосов
/ 24 июня 2009

В следующем коде C ++ я понял, что gcount() возвращает большее число, чем я хотел, потому что getline() использует последний символ новой строки, но не отправляет его во входной поток.

Что я до сих пор не понимаю, так это вывод программы. Для ввода "Test \ n", почему я получаю "est \ n"? Почему моя ошибка влияет на первый символ строки, а не на добавление нежелательного мусора в конец? И почему выходные данные программы расходятся с тем, как строка выглядит в отладчике (как я и ожидал, «Test \ n»)?

#include <fstream>
#include <vector>
#include <string>
#include <iostream>

using namespace std;

int main()
{
    const int bufferSize = 1024;
    ifstream input( "test.txt", ios::in | ios::binary );

    vector<char> vecBuffer( bufferSize );
    input.getline( &vecBuffer[0], bufferSize );
    string strResult( vecBuffer.begin(), vecBuffer.begin() + input.gcount() );
    cout << strResult << "\n";

    return 0;
}

Ответы [ 4 ]

12 голосов
/ 24 июня 2009

Я также продублировал этот результат, Windows Vista, Visual Studio 2005 SP2.

Когда я выясню, что, черт возьми, происходит, я обновлю этот пост.

edit : Хорошо, поехали. Проблема (и разные результаты, которые люди получают) от \ r. Что происходит, вы звоните input.getline и помещаете результат в vecBuffer. Функция getline удаляет \ n, но оставляет \ r на месте.

Затем вы переносите vecBuffer в строковую переменную, но используете функцию gcount из input, что означает, что вы получите один символ слишком много, потому что входная переменная все еще содержит \ n, а vecBuffer - нет.

Результирующий strResult:

-       strResult   "Test"
        [0] 84 'T'  char
        [1] 101 'e' char
        [2] 115 's' char
        [3] 116 't' char
        [4] 13 '␍'  char
        [5] 0   char

Таким образом, затем выводится «Test», за которым следует возврат каретки (переводит курсор назад в начало строки), нулевой символ (перезаписывая T) и, наконец, \ n, который правильно ставит курсор на новая строка.

Таким образом, вы должны либо удалить \ r, либо написать функцию, которая получает длину строки непосредственно из vecBuffer, проверяя наличие нулевых символов.

6 голосов
/ 24 июня 2009

Я продублировал проблему Томми в системе Windows XP Pro с пакетом обновления 2 (SP2) с помощью кода, скомпилированного с использованием Visual Studio 2005 с пакетом обновления 2 (на самом деле он говорит «Версия 8.0.50727.879»), созданного как консольный проект.

Если мой файл test.txt содержит только «Test» и CR, программа выдает «est» (обратите внимание на начальный пробел) при запуске.

Если бы мне пришлось сделать дикое предположение, я бы сказал, что в этой версии реализации есть ошибка, при которой он обрабатывает символ перевода строки Windows так, как его следует обрабатывать в Unix (как «переход к началу той же строки "), а затем стирает первый символ, содержащий часть следующего приглашения или что-то в этом роде.


Обновление: Немного поиграв с этим, я уверен, что именно так и происходит. Если вы посмотрите на strResult в отладчике, вы увидите, что в конце он скопировал десятичное значение 13. Это CR, который в Windows-land равен '\ n', а везде - "возврат к началу строки". Если я вместо этого изменю ваш конструктор на:

string strResult (vecBuffer.begin (), vecBuffer.begin () + input.gcount () - 1);

... (так, чтобы CR не копировался), тогда он печатает «Test», как и следовало ожидать.

2 голосов
/ 24 июня 2009

Я почти уверен, что T на самом деле записывается, а затем перезаписывается. Запуск той же программы в окне rxvt (cygwin) дает ожидаемый результат. Вы можете сделать пару вещей. Если вы откроете бинарный файл ios :: bin, он автоматически преобразует \ r \ n в \ n, и все будет работать так, как вы ожидаете.

Вы также можете открыть свой текстовый файл в бинарном редакторе, нажав на маленькую стрелку вниз на кнопке открытия диалогового окна открытия файла и выбрав открыть с помощью ...-> Бинарный редактор. Это позволит вам посмотреть на ваш файл и убедиться, что он действительно имеет \ r \ n, а не просто \ n.

Edit: Я перенаправил вывод в файл, и он пишет:

Test\r\0\r\n

Причина, по которой вы получаете \ 0, заключается в том, что gcount возвращает 6 (6 символов были удалены из потока), но конечный разделитель не копируется в буфер, вместо этого используется '\ 0'. когда вы создаете строку, вы на самом деле говорите, что она должна включать '\ 0'. std :: string не имеет проблем со встроенным 0 и выводит его в соответствии с запросом. Некоторые оболочки, по-видимому, выводят пустой символ и перезаписывают T, в то время как другие ничего не делают, и вывод выглядит нормально, но все еще, вероятно, неправильный, потому что он имеет встроенный '\ 0'

cout << strResult.c_str() << "\n";

Изменение последней строки на это остановит \ 0 и также даст ожидаемый результат.

1 голос
/ 24 июня 2009

Я протестировал ваш код с помощью Visual Studio 2005 с пакетом обновления 2 (SP2) в Windows XP Pro с пакетом обновления 3 (32-разрядная версия), и все работает нормально.

...