c++ - por - Leer desde ifstream no leerá espacios en blanco
leer una letra en c++ (8)
Estoy implementando un lexer personalizado en C ++ y cuando intento leer en espacios en blanco, el ifstream no lo leerá. Estoy leyendo personaje por personaje usando >>
, y todo el espacio en blanco se ha ido. ¿Hay alguna manera de hacer que el ifstream mantenga todo el espacio en blanco y me lo lea? Sé que al leer cadenas completas, la lectura se detendrá en espacios en blanco, pero esperaba que al leer carácter por personaje, evitaría este comportamiento.
Intento: .get()
, recomendado por muchas respuestas, pero tiene el mismo efecto que std::noskipws
, es decir, ahora obtengo todos los espacios, pero no el carácter de nueva línea que necesito para lex algunas construcciones.
Aquí está el código ofensivo (comentarios extendidos truncados)
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;
}
Estoy rompiendo la línea while
y observando cada valor de la current
medida que entra, y /r
o /n
definitivamente no están entre ellos, la entrada simplemente salta a la siguiente línea en el archivo de entrada.
¿Ajustar el flujo (o su búfer, específicamente) en un std::streambuf_iterator
? Eso debería ignorar todo el formato y también proporcionarle una buena interfaz de iterador.
Alternativamente, un enfoque mucho más eficiente e infalible podría utilizar la API de Win32 (o Boost) para mapear el archivo en memoria. Luego puede atravesarlo utilizando punteros simples y tiene la garantía de que el tiempo de ejecución no omitirá ni convertirá nada.
¿Por qué no usar simplemente getline
?
Obtendrá todos los espacios en blanco, y aunque no obtendrá los caracteres de fin de línea, aún sabrá dónde se encuentran :)
El operador >> come espacios en blanco (espacio, tabulador, nueva línea). Usa yourstream.get()
para leer cada personaje.
Editar:
Cuidado: las plataformas (Windows, Un * x, Mac) difieren en la codificación de nueva línea. Puede ser ''/ n'', ''/ r'' o ambos. También depende de cómo abra el flujo de archivos (texto o binario).
Editar (analizando código):
Después
while(input.get(current) && current != L''/n'');
continue;
habrá un /n
en current
, si no se llega al final del archivo. Después de eso continúas con el bucle while más lejano. Allí el primer carácter en la siguiente línea se lee en current
. ¿No es eso lo que querías?
Intenté reproducir tu problema (usando char
y cin
lugar de wchar_t
y 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;
}
Este programa, aplicado a sí mismo, elimina todos los comentarios de línea de C ++ en su salida. El bucle while interno no consume todo el texto hasta el final del archivo. Por favor, tenga en cuenta la putback(c)
. Sin eso no aparecería la nueva línea.
Si no funciona igual para wifstream
, sería muy extraño, excepto por un motivo: cuando el archivo de texto abierto no se guarda como 16bit char y el carácter /n
char termina en el byte incorrecto ...
Hay un manipulador para deshabilitar el comportamiento de omisión de espacios en blanco:
stream >> std::noskipws;
Los extractores de flujo se comportan igual y omiten los espacios en blanco.
Si desea leer cada byte, puede usar las funciones de entrada sin formato, como stream.get(c)
.
Podrías abrir la secuencia en modo binario:
std::wifstream stream(filename, std::ios::binary);
Perderá cualquier operación de formateo que me proporcione la secuencia si hace esto.
La otra opción es leer la secuencia completa en una cadena y luego procesar la cadena:
std::wostringstream ss;
ss << filestream.rdbuf();
Por supuesto, obtener la cadena de ostringstream requiere una copia adicional de la cadena, por lo que podría considerar cambiar esto en algún momento para usar una secuencia personalizada si se siente aventurero. EDITAR: alguien más menciona istreambuf_iterator, que probablemente sea una mejor manera de hacerlo que leer todo el flujo en una cadena.
Solo puedes envolver la secuencia en un std :: streambuf_iterator para obtener datos con todos los espacios en blanco y nuevas líneas como esta.
/*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();
}
Terminé simplemente abriendo la API de Windows y usándola para leer todo el archivo en un búfer primero, y luego leer ese búfer carácter por carácter. Gracias chicos.