Ответ 1
В итоге я просто взломал Windows API и использовал его, чтобы сначала прочитать весь файл в буфер, а затем прочитать этот символ буфера по символу. Спасибо, ребята.
Я реализую пользовательский лексер на С++, и когда вы пытаетесь прочитать в пробеле, ifstream не будет его читать. Я читаю символ по символу, используя >>
, и все пробелы исчезли. Есть ли способ сделать ifstream держать все пробелы и прочитать его мне? Я знаю, что при чтении целых строк чтение будет останавливаться в пробеле, но я надеялся, что, читая характер по характеру, я бы избегал этого поведения.
Попытка: .get()
, рекомендованная многими ответами, но имеет тот же эффект, что и std::noskipws
, т.е. теперь я получаю все пробелы, но не символ новой строки, который мне нужен для каких-то конструкций.
Здесь код нарушения (расширенные комментарии укорочены)
while(input >> current) {
always_next_struct val = always_next_struct(next);
if (current == L' ' || current == L'\n' || current == L'\t' || current == L'\r') {
continue;
}
if (current == L'/') {
input >> current;
if (current == L'/') {
// explicitly empty while loop
while(input.get(current) && current != L'\n');
continue;
}
Я ломаюсь на строку while
и просматриваю каждое значение current
по мере его появления, а \r
или \n
определенно не среди них: вход просто переходит к следующей строке в входной файл.
В итоге я просто взломал Windows API и использовал его, чтобы сначала прочитать весь файл в буфер, а затем прочитать этот символ буфера по символу. Спасибо, ребята.
Существует манипулятор, позволяющий отключить поведение пропущенных пробелов:
stream >> std::noskipws;
Оператор → ест пробел (пробел, табуляция, новая строка). Используйте yourstream.get()
для чтения каждого символа.
Edit:
Остерегайтесь: платформы (Windows, Un * x, Mac) отличаются кодировкой новой строки. Это могут быть "\n", "\ r" или оба. Это также зависит от того, как вы открываете поток файлов (текстовый или двоичный).
Изменить (анализ кода):
После
while(input.get(current) && current != L'\n');
continue;
в current
будет \n
, если не достигнут конец файла. После этого вы продолжаете цикл outmost while. Там первый символ следующей строки считывается в current
. Разве это не то, что вы хотели?
Я попытался воспроизвести вашу проблему (используя char
и cin
вместо wchar_t
и wifstream
):
//: get.cpp : compile, then run: get < get.cpp
#include <iostream>
int main()
{
char c;
while (std::cin.get(c))
{
if (c == '/')
{
char last = c;
if (std::cin.get(c) && c == '/')
{
// std::cout << "Read to EOL\n";
while(std::cin.get(c) && c != '\n'); // this comment will be skipped
// std::cout << "go to next line\n";
std::cin.putback(c);
continue;
}
else { std::cin.putback(c); c = last; }
}
std::cout << c;
}
return 0;
}
Эта программа, применяемая к себе, исключает все строки комментариев на С++ в ее выходе. Внутренний цикл while не съедает весь текст до конца файла. Обратите внимание на инструкцию putback(c)
. Без этого новая строка не появится.
Если это не работает для wifstream
, это было бы очень странно, за исключением одной причины: когда открытый текстовый файл не сохраняется как 16 бит char, а \n
char заканчивается неправильный байт...
Оберните поток (или его буфер, в частности) в std::streambuf_iterator
? Это должно игнорировать все форматирование, а также дать вам хороший интерфейс итератора.
В качестве альтернативы, гораздо более эффективный и надежный подход мог бы просто использовать Win32 API (или Boost) для карты памяти. Затем вы можете пересечь его с помощью простых указателей, и вам гарантировано, что во время выполнения ничего не будет пропущено или преобразовано.
Извлекители потока ведут себя одинаково и пропускают пробелы.
Если вы хотите прочитать каждый байт, вы можете использовать неформатированные функции ввода, например stream.get(c)
.
Почему бы просто не использовать getline
?
Вы получите все пробелы, и пока вы не получите символы конца строк, вы все равно будете знать, где они лежат:)
Вы можете открыть поток в двоичном режиме:
std::wifstream stream(filename, std::ios::binary);
Вы потеряете любые операции форматирования, предоставленные моим потоком, если вы это сделаете.
Другой вариант - прочитать весь поток в строке, а затем обработать строку:
std::wostringstream ss;
ss << filestream.rdbuf();
Конечно, получение строки из ostringstream требует дополнительной копии строки, поэтому вы можете в какой-то момент изменить ее, чтобы использовать пользовательский поток, если вы чувствуете себя авантюрным. EDIT: кто-то еще упоминает istreambuf_iterator, что, вероятно, лучший способ сделать это, чем чтение всего потока в строку.
Вы можете просто обернуть поток в std :: streambuf_iterator, чтобы получить данные со всеми пробелами и символами новой строки, как это.
/*Open the stream in default mode.*/
std::ifstream myfile("myfile.txt");
if(myfile.good()) {
/*Read data using streambuffer iterators.*/
vector<char> buf((std::istreambuf_iterator<char>(myfile)), (std::istreambuf_iterator<char>()));
/*str_buf holds all the data including whitespaces and newline .*/
string str_buf(buf.begin(),buf.end());
myfile.close();
}
Просто используйте getline.
while (getline(input,current))
{
cout<<current<<"\n";
}
По умолчанию этот флаг пропуска уже установлен для объекта ifstream, поэтому мы должны отключить его. Объект ifstream имеет эти флаги по умолчанию из-за std::basic_ios :: init, вызываемого для каждого нового объекта ios_base (более подробно). Подойдет любое из следующего:
in_stream.unsetf(std::ios_base::skipws);
in_stream >> std::noskipws; // Using the extraction operator, same as below
std::noskipws(in_stream); // Explicitly calling noskipws instead of using operator>>
Другие флаги перечислены в cpp reference.