Tengo un archivo que se supone que debo leer cada línea y almacenarlo en una estructura de matrices, estoy leyendo bien el archivo pero los resultados son mixtos porque los datos están separados por comas, ¿cómo hago para que lea la línea separada por comas?

struct Data{
    std::string a;
    std::string b;
    std::string c;
}

int main(){
Data cd[100];

std::ifstream inFile;  
std::string data;
inFile.open("data.txt");                 
if(inFile.good()){                       
    while(getline(inFile, data)){ //when I add, ',' right after data nothing shows up 
        std::stringstream ss(data);     
        ss >>cd[0].a >> cd[0].b >> cd[0].c;
        std::cout <<cd[0].a<<std::endl;
    }
}
else{
std::cout << "can't open" << std::endl;
}

inFile.close();    
//close the file
return 0;
}
c++
-1
mashertmos 27 ene. 2016 a las 06:27

3 respuestas

La mejor respuesta

Le falta un punto y coma después de su struct y también siempre sobrescribe la primera estructura en su matriz de 100 estructuras durante cada ciclo: la solución más fácil es hacer un contador de índice, que aumentará con cada línea. Para dividir en un delimitador definido por el usuario, la biblioteca estándar de C ++ no tiene una función predefinida para hacer esto: o está usando boost o implementando su propia solución.

Es decir, algo como esto

#include <string>
#include <fstream>
#include <iostream>

struct Data {
  std::string a;
  std::string b;
  std::string c;
};

int main() {
  Data cd[100];
  size_t index = 0;
  std::ifstream inFile;
  std::string data;
  inFile.open("data.txt");
  if (inFile.good()) {
    while (getline(inFile, data)) { //when I add, ',' right after data nothing shows up 
      std::string::size_type startPos = data.find_first_not_of(",", 0); /* skip leading delimiters */
      std::string::size_type endPos = data.find_first_of(",", startPos); /* find first real delimiter */
      cd[index].a = data.substr(startPos, endPos - startPos); /* save substring */
      startPos = data.find_first_not_of(",", endPos); /* find next start position */
      endPos = data.find_first_of(",", startPos); /* find next endPosition */
      cd[index].b = data.substr(startPos, endPos - startPos); /* save substring */
      startPos = data.find_first_not_of(",", endPos); /* find next start position */
      endPos = data.find_first_of(",", startPos); /* find next endPosition */
      cd[index].c = data.substr(startPos, endPos - startPos); /* save substring */
      ++index;
    }
  }
  else {
    std::cout << "can't open" << std::endl;
  }
  inFile.close();
  //close the file
  return 0;
}

Limitación: de acuerdo con su estructura, se asume que hay 3 valores por línea en su archivo, y de acuerdo con su matriz de estructuras, no debe haber más de 100 líneas. Recomendaría usar un std :: vector agradable en lugar de un simple C antiguo -matriz de estilo.

0
Constantin 27 ene. 2016 a las 05:14

Usted había respondido que estaba obligado a utilizarlo como estructura, bueno, todavía puede hacerlo. Hay diferentes enfoques para esto. Ahora, de acuerdo con su pregunta y el ejemplo que ha proporcionado, su estructura tiene 3 cadenas y necesita matrices de ellas; supongamos que cada línea de texto tiene 3 valores de cadena separados por una coma. Creé un archivo de texto llamado custom.txt y este es su contenido:

custom.txt

Hello,world,how
are,you,today

Cada línea tiene exactamente tres palabras separadas por comas sin espacios: ahora demostraré cómo usar mi función de utilidad que mostré en mi respuesta anterior para dividir una cadena después de leer una línea de texto y luego analizarla mientras llena su matriz de estructuras.

#include <iostream>
#include <fstream>
#include <tchar.h>
#include <conio.h>
#include "Utility.h"

struct MyStruct {
    std::string str1;
    std::string str2;
    std::string str3;
}; // MyStruct

void parseLine( std::string& strLine, std::vector<std::string>& results, MyStruct& myStruct ) {
    results = Utility::splitString( strLine, std::string( "," ) );
    myStruct.str1 = results[0];
    myStruct.str2 = results[1];
    myStruct.str3 = results[2];
} // parseLine

bool readLine( std::fstream& in, std::string& line ) {
    if ( in.eof() ) {
        return false;
    }
    std::getline( in, line );
    return true;
} // readLine

int main() {
    std::string filename( "custom.txt" );
    std::string strLine;
    sd::fstream fin;

    fin.open( filename.c_str(), std::ios_base::in ) {
    if ( !fin.is_open() ) {
        std::cout << "Error reading in file " << filename << std::endl;
        return -1;
    }

    std::vector<std::string> results;
    // Since I only have 2 lines in this file 
    MyStruct myStruct[2];

    int line = 0;
    while ( readLine( fin, strLine ) ) {
        parseLine( strLine, results, myStruct[line] );
        line++;
    }

    std::cout << "Results: " << std::endl;
    for ( unsigned u = 0; u < 2; u++ ) {
        std::cout << myStruct[u].str1 << " " << myStruct[u].str2 << " " << myStruct[u].str3 << std::endl;
    }
    std::cout << std::endl;

    std::cout << "Press any key to quit." << std::endl;
    _getch();

    return 0;
} // main

Lo único que debe tener en cuenta con esta implementación en particular es que los bucles se establecen de acuerdo con exactamente cuántos valores almacenados hay en su estructura (3 cadenas) y cuántos en la matriz deben coincidir con el número de líneas en el archivo guardado . Esto debe conocerse de antemano. Si no sabe de antemano cuántas líneas hay en el archivo, no creo que pueda usar una matriz, ya que tienen que ser un tipo integral constante en el momento de la compilación. Si estaba utilizando memoria dinámica con punteros en el montón, podría automatizar este sistema sin saber cuántos elementos hay en una sola línea o sin saber cuántas líneas hay en un archivo en particular.

0
Francis Cugler 29 ene. 2016 a las 13:41

Esto puede ayudarlo en muchas situaciones. Abra su archivo y lea el texto línea por línea en un std :: string. Entonces esta útil función de utilidad funciona muy bien

Utility.h

#ifndef UTILITY_H
#define UTILITY_H

#include <string>

class Utility {
public:    
    static std::vector<std::string> splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty = true );

private:
    Utility(); // Private - Not A Class Object
    Utility( const Utility& c ); // Not Implemented
    Utility& operator=( const Utility& c); // Not Implemented
};

#endif // UTILITY_H

Utility.cpp

#include "Utility.h"

std::vector<std::string> Utility::splitString( const std::string& strStringToSplit, const std::string& strDelimiter, const bool keepEmpty ) {

    std::vector<std::string> vResult;
    if ( strDelimeter.empty() ) {
        vResult.push_back( strStringToSplit );
        return vResult;
    }

    std::string::const_iterator itSubStrStart = strStringToSplit.begin(), itSubStrEnd;
    while ( true ) {
        itSubStrEnd = search( itSubStrStart, strStringToSplit.end(), strDelimiter.begin(), strDelimiter.end() );
        std::string strTemp( itSubStrStart, itSubStrEnd );
        if ( keepEmpty || !strTemp.empty() ) {
            vResult.push_back( strTemp );
        }

        if ( itSubStrEnd == strStringToSplit.end() ) {
            break;
        }

        itSubStrStart = itSubStrEnd + strDelimeter.size();
    }

    return vResult;

} // stringToSplit

Y para usar esto en cualquier clase o función, simplemente incluye Utility.h

main.cpp

#include <iostream>
#include "Utility.h"    

int main() {

    std::string welcome( "Hello,World,How,Are,You,Today,!" );
    std::vector<std::string> result;
    result = Utility::splitString( welcome, std::string( "," ) );

    for ( unsigned u = 0; u < result.size(); u++ ) {
        std::cout << result.at( u ) << std::endl;
    }

    return 0;
}

Y su impresión debería ser:

Hello
World
How
Are
You
Today
!

Donde cada palabra está separada por comas sin espacios. Puede usar cualquier carácter como su delimitador de definición, ya sea un espacio, dos puntos, punto y coma, guión, etc. Esta es una función de utilidad muy útil y práctica que se puede usar una y otra vez en muchas situaciones; especialmente cuando intenta analizar archivos o cadenas. Ahora debe tener en cuenta el hecho de que esto analizará cadenas. Todavía tiene que leer el archivo línea por línea y almacenar cada línea en una cadena, luego crear un vector de cadenas que almacenará los resultados de esta función.

Puede que al principio no parezca intuitivo tener esta función en una clase, pero tengo alrededor de una docena de funciones de utilidad útiles para convertir cadenas a mayúsculas y minúsculas, convertir una cadena a diferentes tipos de datos básicos como int, floats e incluso para convertir una cadena de valores separados por comas en objetos vec2, vec3 y vec4, donde todas estas funciones se declaran como estáticas en una clase que no puede instanciar. Actúa como un contenedor o envoltorio casi como un espacio de nombres. En otras palabras, la mayoría de mis funciones de manipulación de cadenas independientes que se usarían mucho, terminaré conteniéndolas aquí dentro de esta clase.

0
Francis Cugler 27 ene. 2016 a las 05:43