2009-09-18 13 views
13

Tengo algunos datos de cadena en el siguiente formato: "Ronit", "abc" "DEFGH", "abcdef" fdfd",de análisis de archivos en C++

Puede alguien sugerir un buen código en C++ para devolver el fichas separadas por comas, cuando las comas no están dentro de una cadena?

es decir, debe volver

  1. "Ronit"
  2. "abc" "DEFGH"
  3. "abcdef" fdfd"

a ser más clara

Gracias a todos ustedes por amable ayuda.

A continuación es mi archivo de ejemplo que se da como entrada,

Primera línea me dirá cuántas columnas tengo #

Nombre1, Nombre2, Nombre3, Nombre4

"usuario1", "usuario, usuario2", "User3", "userrr

rrr4",

"usuario1", "usuario2", "User3", "nos

er4" ,

"", "usuario usuario1, 2", "User3", "user4"

"", "usuario2,"", "", #

Abajo es una salida de archivo csv, por favor dame código de compilación, para que pueda probar, gracias de nuevo por tu amable ayuda.

primera, 1) usuario1, 2) de usuario, usuario2 3) usuario3 4) userrrr4

Nota RR4 Fila está en línea siguiente.

segunda fila, 1) usuario1 2) usuario2 3) User3 4) nos er4

nota er4 está en línea siguiente.

tercera fila, 1) usuario1 2) del usuario, 2 3) User3 4) user4

fila 4thr 1) 2) usuario2 3) 4)

+3

código mejor que qué? – GManNickG

+0

quizás quiso decir "bueno". –

+1

Creo que hay un error en el formato de entrada. ¿Dónde está la cita de finalización para "abcdef,? –

Respuesta

-1

No es la mejor manera, pero se puede utilizar el strtok función.

+2

strtok() no es una buena idea. Modifica la cadena de entrada mientras la analiza. Hay métodos más limpios que eso. –

+0

@Martin York, es por eso que dije" no de la mejor manera " "Gracias por mencionar eso. –

+1

Siempre puede hacer una copia de la cadena de entrada para evitar modificarla – Pawka

0

Esto devuelve la división tokens exactamente como lo pidió:

using namespace std; 
vector<string> splitqc(std::string const& s) { 
vector<string> tokens; 
char last=0; 
unsigned start=0;  
for (unsigned i=0,n=s.length;i!=n;++i) { 
    char c=s[i]; 
    if (c==',' && last='"') { 
    tokens.push_back(s.substr(start,(i-1)-start)); 
    start=i+1; 
    } 
    last=s[i]; 
} 
return tokens; 
} 

Aquí es una instalación más general (el funtor f es llamado con cada ficha, tenga en cuenta que no va a tener la cita cerca que es parte de su delimitador; se tendría que añadir que usted mismo):

template <class Func> 
inline void split_noquote(
    const std::string &csv, 
    Func f, 
    const std::string &delim="," 
    ) 
{ 
    using namespace std; 
    string::size_type pos=0,nextpos; 
    string::size_type delim_len=delim.length(); 
    if (delim_len==0) delim_len=1; 
    while((nextpos=csv.find(delim,pos)) != string::npos) { 
     if (! f(string(csv,pos,nextpos-pos))) 
      return; 
     pos=nextpos+delim_len; 
    } 
    if (csv.length()!=0) 
     f(string(csv,pos,csv.length()-pos)); 
} 

Uso: split_noquote (s, func, "\" ")

+1

La primera función no compilará –

1

Esto se parece a analizar un archivo CSV a mí (aunque sea no techni un archivo) - puede echar un vistazo al this question and answer.

1

Lo siguiente supondrá que la entrada proviene de alguna secuencia (después de todo, tenía una ficha de C++). Si ese no es el caso, investiga las secuencias de cadenas.

std::string read_quoted_string(std::istream& is) 
{ 
    is >> std::ws; 
    std::string garbage; 
    std::getline(is,garbage,'"'); // everything up to opening quote 
    if(!garbage.empty()) throw format_error("garbage outside of quotes", garbage); 
    if(!is.good()) return std::string(); 

    std::string a_string; 
    std::getline(is,a_string,'"'); // the string up to closing quote 
    if(!is) return std::string(); 
    return a_string; 
} 

std::vector<std::string> split_input(std::istream& is) 
{ 
    std::vector<std::string> result; 
    while(is) { 
    const std::string& a_string = read_quoted_string(is); 
    if(is) { 
     result.push_back(a_string); 
     is >> std::ws; 
     std::string garbage; 
     std::getline(is,garbage,','); // next delimiter 
     if(!garbage.empty()) throw format_error("garbage outside of quotes", garbage); 
    } 
    } 
    if(!is.eof()) throw format_error("error reading token", a_string); 
    return result; 
} 

Esto no es lo más rápido que puede tener, pero es una solución simple y muy probablemente lo suficientemente rápida.

+1

-1 para publicar código que ni siquiera compilará. –

+0

@Vijay: Gracias por señalar a cabo estos dos errores cortar de una manera tan amistosa. Supongo que esto fue tan difícil de solucionar que, incluso si alguien logra arreglarlo, sus intentos sin duda habría roto el código hasta el punto en que no haría lo que ya se especificó. Por supuesto, un error tan terrible ciertamente justifica un voto negativo. De todos modos, gracias a su amable ayuda lo resolví y lo arreglé__. ¿Sería usted ahora tan amable como para eliminar su voto abajo? Gracias de antemano.Ah, y por cierto: mientras que la venganza es dulce, los dulces pueden hacer que tus dientes se pudran. – sbi

+0

'' ¿Sería el segundo votante a favor, tan amable de decirme qué pasa con mi solución? – sbi

-1

No creo que se pueda analizar algo como "abcdef, fdfd" Esto es ilegal, para cualquier idioma y para cualquier formato de datos, porque una de las citas no se termina. Debe ser "abcdef, fdfd ." Teniendo en cuenta que todas las cadenas están terminados correctamente, la siguiente función dará la salida que desea

std::istream& tokenize_quoted_strings(std::istream& in, 
           std::string& dest, 
           char delim) 
{ 
    dest.erase(); 
    char ch = 0; 
    bool in_quotes = false; 
    while (in) 
    { 
     if (!in.get(ch)) break;  
     if (!in_quotes && ch == delim) break; 
     dest.push_back(ch); 
     if (ch == '"') in_quotes = !in_quotes; 
    } 
    return in; 
} 

la siguiente función utiliza tokenize_quoted_strings para dividir una cadena en un vector de símbolos:.

typedef std::vector<std::string> StringList; 

void tokenize_line(const std::string& line, 
      StringList& tokens) 
{ 
    std::istringstream iss(line); 
    std::string token; 
    tokens.clear(); 
    while (tokenize_quoted_strings(iss, token, ',')) 
    tokens.push_back(token); 
} 

nosotros edad:

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

int main() 
{ 
    std::fstream in("test.txt", std::ios_base::in); 
    std::string line; 
    StringList tokens; 
    while (getline(in, line)) 
    { 
     tokenize_line(line, tokens); 
     size_t sz = tokens.size(); 
     for (size_t i=0; i<sz; ++i) 
    std::cout << (i+1) << ") " << tokens[i] << ' '; 
     std::cout << '\n'; 
    } 
    return 0; 
} 

Tenga en cuenta que no le importan las citas escapadas del estilo C.

+0

Se solucionó el error informado por sbi. –

+0

Gracias vijay, me acabo de dar cuenta de mi error ¡Gracias! –

+0

@Ronit Esto es una especie de trabajo a domicilio. De todos modos, he actualizado mi código con tu última entrada. –

1

Simplemente descarga boost y usa boost.tokenizer.
Es la mejor solución que existe.

16

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

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

int main() 
{ 
    std::deque<std::string> word_list; 
    strtk::for_each_line("data.txt", 
         [&word_list](const std::string& line) 
         { 
          const std::string delimiters = "\t\r\n ,,.;:'\"" 
                  "[email protected]#$%^&*_-=+`~/\\" 
                  "()[]{}<>"; 
          strtk::parse(line,delimiters,word_list); 
         }); 

    std::cout << strtk::join(" ",word_list) << std::endl; 

    return 0; 
} 

Más ejemplos se pueden encontrar Here