Почему начало моей строки исчезает?
Вопрос
В следующем коде C++ я понял, что gcount()
возвращал большее количество, чем мне хотелось, потому что getline()
потребляет последний символ новой строки, но не отправляет его во входной поток.
Однако чего я до сих пор не понимаю, так это вывода программы.Почему для ввода «Тест » я получаю «est »?Почему моя ошибка влияет на первый символ строки, а не добавлять нежелательный мусор в конец?И почему вывод программы не совпадает с тем, как строка выглядит в отладчике («Test », как я и ожидал)?
#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;
}
Решение
Я также продублировал этот результат, 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
Итак, "Тест" печатается с последующим возвратом каретки (переводит курсор назад в начало строки), нулевым символом (перезаписывая T) и, наконец, \ n, что правильно ставит курсор на новую строку.
Таким образом, вы должны либо удалить \ r, либо написать функцию, которая получает длину строки непосредственно из vecBuffer, проверяя наличие нулевых символов.
Другие советы
Я продублировал проблему Томми в системе Windows XP Pro Service Pack 2 с кодом, скомпилированным с использованием Visual Studio 2005 SP2 (на самом деле там написано «Версия 8.0.50727.879»), созданным как консольный проект.
Если мой файл test.txt содержит только «Test» и CR, при запуске программа выдает «est» (обратите внимание на начальный пробел).
Если бы мне пришлось сделать необдуманное предположение, я бы сказал, что в этой версии реализации есть ошибка, из-за которой она обрабатывает символ новой строки Windows так, как он должен обрабатываться в Unix (как «перейти в начало той же строки»). символ), а затем он удаляет первый символ, содержащий часть следующего приглашения или что-то в этом роде.
Обновлять:Немного поиграв с этим, я уверен, что именно это и происходит.Если вы посмотрите на strResult в отладчике, вы увидите, что в конце он скопировал десятичное значение 13.Это CR, который в Windows-land — это « », а везде — «возврат к началу строки».Если вместо этого я изменю ваш конструктор на следующее:
строка strResult( vecBuffer.begin(), vecBuffer.begin() + input.gcount() - 1);
... (чтобы CR не копировался), затем он печатает «Тест», как и следовало ожидать.
Я почти уверен, что буква T на самом деле записывается, а затем перезаписывается.Запуск той же программы в окне rxvt (cygwin) дает ожидаемый результат.Вы можете сделать пару вещей.Если вы избавитесь от ios::binary в своем открытом файле, он автоматически преобразует в , и все будет работать так, как вы ожидаете.
Вы также можете открыть свой текстовый файл в двоичном редакторе, щелкнув маленькую стрелку вниз на кнопке открытия диалогового окна открытия файла и выбрав «Открыть с помощью...->Двоичный редактор».Это позволит вам просмотреть файл и убедиться, что он действительно имеет , а не только .
Редактировать:Я перенаправил вывод в файл, и он пишет:
Test\r\0\r\n
Причина, по которой вы получаете \0, заключается в том, что gcount возвращает 6 (6 символов были удалены из потока), но последний разделитель не копируется в буфер, вместо этого копируется «\0».когда вы создаете строку, вы фактически указываете ей включить «\0».std::string не имеет проблем со встроенным 0 и выводит его по запросу.Некоторые оболочки, по-видимому, выводят пустой символ и перезаписывают букву T, в то время как другие ничего не делают, и вывод выглядит нормально, но, вероятно, все равно неверен, поскольку в него встроен символ '\0'.
cout << strResult.c_str() << "\n";
Изменение последней строки на это остановится на \0 и также получит ожидаемый результат.
Я протестировал ваш код с помощью Visual Studio 2005 с пакетом обновления 2 (SP2) на Windows XP Pro с пакетом обновления 3 (32-разрядная версия), и все работает нормально.