CSV mit Zeilenumbruch in Zeile



  • Hi,

    ich möchte ein CSV (bzw. tabbed txt) einlesen, dass ich unter Excel gespeichert habe. Das funktioniert bei mir wie folgt (unter C++ Bulder):

    for (int r = 0; !InputFile.eof(); r++) {
      // Import buffer
      char Buff[BUFF_LEN];
      String Buffer;
    
      // Get lines
      InputFile.getline(Buff, BUFF_LEN);
      Buffer = Buff;
    
      // Insert into string grid
      int p, c = 0;
      do {
        // Get delimiter position
        p = Buffer.Pos("\t");
        // Get cell text
        if (p > 0) {
          MyCell[c++][r] = Buffer.SubString(0,p-1);
          Buffer.Delete(1,p);
        }
        else {
          MyCell[c++][r] = Buffer;
        }
      } while (p > 0);
    }
    

    Jetzt sind in dem Excel-Sheet aber in einigen Zellen Zeilenumbrüche enthalten. Das führt dazu, dass getline nur bis zu eben dieser Stelle einliest und ich dort fälschlicherweise in die nächste Zeile wechsle.
    Die Zelle mit Zeilenumbruch ist in " eingeschlossen, alle anderen nicht. Gibt's nen einfachen Weg daraus?



  • schau dir mal unter http://www.cplusplus.com/reference/iostream/istream/getline/ die referenz zu getline an - du kannst direkt einen delimiter angeben (in deinem Fall '\t'), so dass getline eben nicht mehr am Zeilenumbruch halt macht, sondern genau beim '\t'.
    Damit würden sich auch deine doppelt gemoppelten char[] und std::string buffer erübrigen (es gibt in <string> eine getline-Funktion, die direkt in einen String liest, ohne den Umweg über char zu gehn, daher ist der char[]-Buffer so oder so überflüssig)

    Wenn MyCell[c][r] jeweils ein bereits existierender std::string ist, geht das Ganze dann wie folgt:

    int c = 0;
    while (c < MAX_VALUE_FOR_C) //den solltest du abfragen, um nicht einem nicht existierenden string was zuzuweisen
    {
      std::getline(InputFile, MyCell[c++][r]);
    }
    


  • String ist UnicodeString, nicht std::string. Das verwende ich nur der Bequemheit halber. Und InputFile ist ein fstream. MyCell enthält ebenfalls UnicodeStrings und da getline einen char* erwartet, klappt das soweit ich weiß nicht so einfach...

    Dass man mit getline auch auf andere Zeichen abfragen kann, ist mir schon bewusst. Aber ich weiß vorher nicht, wie viele Spalten ich einlese. Wenn ich getline mit '\t' verwende, muss ich die Zeilenumbrüche wieder manuell rausoperieren. Mit dem gleichen Problem wie vorher...



  • Wenn du Zeilenumbrüche in den Zellen hast, willst du sie doch behalten oder nicht? Dann solltest du sie auch nicht rausoperieren müssen.
    oder gehts hier nur um die Zeilenumbrüche innerhalb von geöffneten ", die du behalten willst, andere dagegen nicht?

    In dem Fall würde ich mit Iteratoren durch den Stream wandern:

    istream_iterator<char> eof;
    int r = 0, c = 0;
    bool in_quotes = false;
    String buffer;
    
    for (istream_iterator<char> pos(InputFile); pos != eof && c < MAX_COL && r < MAX_ROW; ++pos)
    {
      switch (*pos)
      {
        case '\t': //Ende der Zelle erreicht
          MyCell[c++][r] = buffer;
          buffer.clear(); //oder wie auch immer
          break;
        case '\n': 
          if (!in_quotes) //Ende der Zelle und Zeile erreicht
          {
            MyCell[c][r++] = buffer;
            c = 0;
            buffer.clear(); //oder wie auch immer
          }
          else //Zelle geht weiter
          {
            buffer.append('\n'); //oder wie das bei String heißt...
          }
          break;
        case '"': //wechsel von gequteter zelle nach außen ud umgekehrt
          in_quotes = !in_quotes;
          //FALLTHROUGH ist absicht!
        default:
          buffer.append(*pos);
      }
    }
    //nur falls die csv nicht mit \n beendet sein muss:
    if (!buffer.empty())
    {
      MyCell[c][r] = buffer;
      buffer.clear();
    }
    


  • Mit rausoperieren meine ich, die Zeilenumbrüche innerhalb der Zellen von den anderen zu unterscheiden. Die einen sollen dann Bestandteil der Zelle werden, die anderen markieren natürlich das Zeilenende.

    Sowas in der Art hab ich mir auch ausgedacht, nur muss ich dann ja wieder manuell das File zerpflücken (es gibt nämlich auch Zellen mit nur einem Anführungszeichen). Ich hatte eigentlich auf sowas wie fgetcsv aus PHP gedacht. Nur eben in C++...



  • Vielleicht gibts Bibliotheken, die das machen - aber im Standard wirst du sowas vergeblich suchen.
    Allerdings sind Zellen mit nur einmal " drin dann schon ungewöhnlich - wenn du an einem Zeilenende bist kannst du dann ja garnicht mehr wissen, ob das jetzt das Ende einer Zelle mit einem " ist oder ob das nur ein Zeilenumbruch innerhalb der Zelle ist.
    Wenn \t auf jeden Fall das Ende einer Zelle bedeutet, kannst du da dann auch noch in_quotes grundsätlich auf false setzen.
    Manuell das file zerpflüclen musst du so oder so - ob jetzt Zeilenweise oder Zeichenweise ist da schon herzlich egal 😉


Anmelden zum Antworten