2010-02-26 9 views
12

que tienen datos en el siguiente formato:datos de lectura formateados con C++ 's operador corriente >> cuando los datos tiene espacios

 
4:How do you do? 
10:Happy birthday 
1:Purple monkey dishwasher 
200:The Ancestral Territorial Imperatives of the Trumpeter Swan 

El número puede estar en cualquier lugar de 1 a 999, y la cadena es como máximo de 255 caracteres de longitud . Soy nuevo en C++ y parece que algunas fuentes recomiendan extraer datos formateados con un operador de stream >>, pero cuando quiero extraer una cadena se detiene en el primer carácter de espacio en blanco. ¿Hay alguna manera de configurar una secuencia para detener el análisis de una cadena solo en una línea nueva o al final del archivo? Vi que había un método getline para extraer una línea completa, pero aún tengo que dividirlo manualmente [con find_first_of], ¿no?

¿Hay una manera fácil de analizar datos en este formato usando solamente STL?

+2

corrientes en C++ son una de las cosas que odio de C++. – AraK

+0

Como soy nuevo en C++, esperaba que las transmisiones fueran una de esas cosas que finalmente conducen a una epifanía de "oooooh que es inteligente", pero después de su comentario, estoy empezando a pensar que eso nunca sucederá. :( – dreamlax

Respuesta

10

Puede leer el número antes de usar std::getline, que lee de una secuencia y almacena en un objeto std::string. Algo como esto:

int num; 
string str; 

while(cin>>num){ 
    getline(cin,str); 

} 
+0

Que se ve limpio, supongo que será seguro reemplazar 'cin' con un' istream' que me entregan – dreamlax

+0

Si está leyendo un archivo, puede reemplazar cin con un ** ** objeto ifstream válido. – codaddict

+0

Me acaban de dar una secuencia y se espera que mi código analice los datos, los manipule y los escriba en otra secuencia. No creo ninguna secuencia. Supongo que mi filtro no 't se invoca si el 'istream' o' ostream' no es válido, pero al mismo tiempo no creo que sea de mi incumbencia Basura dentro basura fuera :) . . . o tal vez basura en segfault. – dreamlax

2
int i; 
char *string = (char*)malloc(256*sizeof(char)); //since max is 255 chars, and +1 for '\0' 
scanf("%d:%[^\n]s",&i, string); //use %255[^\n]s for accepting 255 chars max irrespective of input size 
printf("%s\n", string); 
 

Su C y también funcionará en C++. scanf proporciona más control, pero no gestión de errores. Entonces use con precaución :).

+0

Parece que la bandera 'm' no está estandarizada, por lo que no puedo usarla. Pero, de nuevo, ¿no se leerá esto solo al primer carácter en blanco en lugar de al final de la línea? – dreamlax

+0

solo está leyendo la primera palabra de la línea, no toda la línea, y tienes un error en tu código: estás proporcionando 'i' pero' scanf' necesita un * pointer * para 'i' (' & i'). – dreamlax

+0

@dreamlax gracias por señalar. Corregido –

2

Simplemente lea los datos línea por línea (línea completa) usando getline y analízalo.
para analizar el uso find_first_of()

+0

¿podría proporcionarnos algún código de muestra, por favor? – Sergei

9

Usted ya ha dicho su std::getline, pero que no mencionar un detalle que probablemente encontrará útil: cuando se llama getline, también puede pasar un parámetro contarla qué personaje tratar como el final de la entrada. Para leer su número, puede utilizar:

std::string number; 
std::string name; 

std::getline(infile, number, ':'); 
std::getline(infile, name); 

Esto pondrá los datos hasta el ':' en number, desechar la ':', y leer el resto de la línea en name.

Si desea utilizar >> para leer los datos, puede hacerlo también, pero es un poco más difícil y se adentra en un área de la biblioteca estándar que la mayoría de las personas nunca toca. Una secuencia tiene asociado locale que se utiliza para formatear números y (lo que es más importante) determinar qué constituye "espacio en blanco". Puede definir su propia configuración regional para definir ":" como espacio en blanco y el espacio ("") como no como espacio en blanco. Indique a la transmisión que use esa configuración regional y le permitirá leer sus datos directamente.

#include <locale> 
#include <vector> 

struct colonsep: std::ctype<char> { 
    colonsep(): std::ctype<char>(get_table()) {} 

    static std::ctype_base::mask const* get_table() { 
     static std::vector<std::ctype_base::mask> 
      rc(std::ctype<char>::table_size,std::ctype_base::mask()); 

     rc[':'] = std::ctype_base::space; 
     rc['\n'] = std::ctype_base::space; 
     return &rc[0]; 
    } 
}; 

Ahora usarlo, que "imbuir" la corriente con una configuración regional:

#include <fstream> 
#include <iterator> 
#include <algorithm> 
#include <iostream> 

typedef std::pair<int, std::string> data; 

namespace std { 
    std::istream &operator>>(std::istream &is, data &d) { 
     return is >> d.first >> d.second; 
    } 
    std::ostream &operator<<(std::ostream &os, data const &d) { 
     return os << d.first << ":" << d.second; 
    } 
} 

int main() { 
    std::ifstream infile("testfile.txt"); 
    infile.imbue(std::locale(std::locale(), new colonsep)); 

    std::vector<data> d; 

    std::copy(std::istream_iterator<data>(infile), 
       std::istream_iterator<data>(), 
       std::back_inserter(d)); 

    // just for fun, sort the data to show we can manipulate it: 
    std::sort(d.begin(), d.end()); 

    std::copy(d.begin(), d.end(), std::ostream_iterator<data>(std::cout, "\n")); 
    return 0; 
} 

Ahora sabes por qué esa parte de la biblioteca está tan descuidado. En teoría, hacer que la biblioteca estándar haga su trabajo es excelente, pero de hecho, la mayoría de las veces es más fácil hacer este tipo de trabajo por su cuenta.

+0

¡Vive para aprender! Gracias, Jerry! –

13

El C++ String Toolkit Library (StrTk) tiene la siguiente solución a su problema:

#include <string> 
#include <deque> 
#include "strtk.hpp" 

int main() 
{ 
    struct line_type 
    { 
     unsigned int id; 
     std::string str; 
    }; 

    std::deque<line_type> line_list; 

    const std::string file_name = "data.txt"; 

    strtk::for_each_line(file_name, 
         [&line_list](const std::string& line) 
         { 
          line_type temp_line; 
          const bool result = strtk::parse(line, 
                  ":", 
                  temp_line.id, 
                  temp_line.str); 
          if (!result) return; 
          line_list.push_back(temp_line); 
         }); 

    return 0; 
} 

Más ejemplos se pueden encontrar Here

Cuestiones relacionadas