Pregunta

Tengo un archivo con los datos listados de la siguiente manera:

0,       2,    10
10,       8,    10
10,       10,   10
10,       16,   10
15,       10,   16
17,       10,   16

Quiero poder ingresar el archivo y dividirlo en tres matrices, en el proceso recortar todos los espacios en exceso y convertir cada elemento en enteros.

Por alguna razón, no puedo encontrar una manera fácil de hacer esto en c ++. El único éxito que he tenido es ingresar cada línea en una matriz, y luego regexear todos los espacios y luego dividirlos. Todo este proceso me llevó unas buenas 20-30 líneas de código y es difícil de modificar, por ejemplo, otro separador (por ejemplo, espacio), etc.

Este es el equivalente en Python de lo que me gustaría tener en C ++:

f = open('input_hard.dat')
lines =  f.readlines()
f.close()

#declarations
inint, inbase, outbase = [], [], []

#input parsing
for line in lines:
    bits = string.split(line, ',')
    inint.append(int(bits[0].strip()))
    inbase.append(int(bits[1].strip()))
    outbase.append(int(bits[2].strip()))

La facilidad de uso de hacer esto en Python es una de las razones por las que me mudé a él en primer lugar. Sin embargo, debo hacer esto en C ++ ahora y odiaría tener que usar mi feo código de línea 20-30.

Cualquier ayuda sería apreciada, ¡gracias!

¿Fue útil?

Solución

Realmente no hay nada malo con fscanf, que es probablemente la solución más rápida en este caso. Y es tan corto y legible como el código de Python:

FILE *fp = fopen("file.dat", "r");
int x, y, z;
std::vector<int> vx, vy, vz;

while (fscanf(fp, "%d, %d, %d", &x, &y, &z) == 3) {
  vx.push_back(x);
  vy.push_back(y);
  vz.push_back(z);
}
fclose(fp);

Otros consejos

No hay necesidad real de usar boost en este ejemplo, ya que las transmisiones funcionarán bien:

int main(int argc, char* argv[])
{
    ifstream file(argv[1]);

    const unsigned maxIgnore = 10;
    const int delim = ',';
    int x,y,z;

    vector<int> vecx, vecy, vecz;

    while (file)
    {
        file >> x;
        file.ignore(maxIgnore, delim);
        file >> y;
        file.ignore(maxIgnore, delim);
        file >> z;

        vecx.push_back(x);
        vecy.push_back(y);
        vecz.push_back(z);
    }
}

Aunque si fuera a usar boost, preferiría la simplicidad de tokenizer para regex ... :)

Algo así como:

vector<int> inint;
vector<int> inbase;
vector<int> outbase;
while (fgets(buf, fh)) {
   char *tok = strtok(buf, ", ");
   inint.push_back(atoi(tok));
   tok = strtok(NULL, ", ");
   inbase.push_back(atoi(tok));
   tok = strtok(NULL, ", ");
   outbase.push_back(atoi(tok));
}

Excepto con comprobación de errores.

std :: getline le permite leer una línea de texto, y puede usar una secuencia de cadena para analizar la línea individual:

string buf;
getline(cin, buf); 
stringstream par(buf);

char buf2[512];
par.getline(buf2, 512, ','); /* Reads until the first token. */

Una vez que obtiene la línea de texto en la cadena, puede usar cualquier función de análisis que desee, incluso sscanf (buf.c_str (), "% d,% d '% d " ;, & amp; i1, & amp ; i2, & amp; i3), mediante el uso de atoi en la subcadena con el entero, o mediante algún otro método.

También puede ignorar los caracteres no deseados en la secuencia de entrada, si sabe que están allí:

if (cin.peek() == ',')
    cin.ignore(1, ',');
cin >> nextInt;  

Si no le importa usar las bibliotecas Boost ...

#include <string>
#include <vector>
#include <boost/lexical_cast.hpp>
#include <boost/regex.hpp>

std::vector<int> ParseFile(std::istream& in) {
    const boost::regex cItemPattern(" *([0-9]+),?");
    std::vector<int> return_value;

    std::string line;
    while (std::getline(in, line)) {
        string::const_iterator b=line.begin(), e=line.end();
        boost::smatch match;
        while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
            return_value.push_back(boost::lexical_cast<int>(match[1].str()));
            b=match[0].second;
        };
    };

    return return_value;
}

Eso extrae las líneas de la secuencia, luego usa la biblioteca Boost :: RegEx (con un grupo de captura) para extraer cada número de las líneas. Ignora automáticamente cualquier cosa que no sea un número válido, aunque eso se puede cambiar si lo desea.

Todavía tiene unas veinte líneas con los #include s, pero puede usarlo para extraer esencialmente cualquier cosa de las líneas del archivo. Este es un ejemplo trivial, estoy usando un código prácticamente idéntico para extraer etiquetas y valores opcionales de un campo de base de datos, la única diferencia importante es la expresión regular.

EDITAR: Vaya, querías tres vectores separados. Pruebe esta ligera modificación en su lugar:

const boost::regex cItemPattern(" *([0-9]+), *([0-9]+), *([0-9]+)");
std::vector<int> vector1, vector2, vector3;

std::string line;
while (std::getline(in, line)) {
    string::const_iterator b=line.begin(), e=line.end();
    boost::smatch match;
    while (b!=e && boost::regex_search(b, e, match, cItemPattern)) {
        vector1.push_back(boost::lexical_cast<int>(match[1].str()));
        vector2.push_back(boost::lexical_cast<int>(match[2].str()));
        vector3.push_back(boost::lexical_cast<int>(match[3].str()));
        b=match[0].second;
    };
};

¿por qué no el mismo código que en python :)?

std::ifstream file("input_hard.dat");
std::vector<int> inint, inbase, outbase;

while (file.good()){
    int val1, val2, val3;
    char delim;
    file >> val1 >> delim >> val2 >> delim >> val3;

    inint.push_back(val1);
    inbase.push_back(val2);
    outbase.push_back(val3);
}

Si desea poder escalar a formatos de entrada más duros, debe considerar el espíritu, impulsar la biblioteca del combinador de analizadores.

Esta página tiene un ejemplo que casi hace lo que necesita (con reales y un vector)

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top