Laden...

CSV Parser - Ist ein Record verteilt auf mehrere Zeilen

Erstellt von Repac3r vor 8 Jahren Letzter Beitrag vor 8 Jahren 2.411 Views
R
Repac3r Themenstarter:in
57 Beiträge seit 2014
vor 8 Jahren
CSV Parser - Ist ein Record verteilt auf mehrere Zeilen

Schönen Abend euch,

es gab einen kleineren Bug in unserem CSV-Parser. Es kann relativ schlecht mit Daten umgehen, sobald ein Record sich über mehrere Zeilen verteilt.
Das Problem habe ich gelöst, nur bin ich mir der Umsetzung nicht zufrieden. Für mich sieht es einfach "dirty" aus. Vlt. hat ein erfahrerener Programmierer einen besseren Ansatz, wäre jedenfalls sehr dankbar.

So, nun zur Erklärung, es gibt Folgendes (nur ein Beispiel):*TextBegrenzer (im Code = FieldBeginsWith & FieldEndsWith = " *FeldBegrenzer (im Code = FieldSeparators = |

Die Werte die mir mit TextSourceDescription übergeben worden, sind leider vorgegeben und können nicht geändert werden.

Ich muss jetzt folgende Fälle parsen können:


Fall1:
"ID"|"Name"
"0"|"Marcel"

-> Einzeiliger Record

Fall 2:
ID|Name
0|Marcel

-> Einzeiliger Record

Fall3:

"ID"|Name         <- (kann auch anders herum sein)
"0"|Marcel

-> Einzeilige Record

Fall4:

"ID"|"Name"
"0"|"Ein sehr
langer name"

-> Merhzeiliger Record

Fall5:

"ID"|"Name"
"0"|"Ein sehr
\"langer\" name"

-> Merhzeiliger Record

Das sind die Fälle die ich abdecken muss.

Was mein Algorithmus macht, ist einfach erklärt:
Ist das letzte Zeichen ein Textbegrenzer? - Wenn Nein, möglicher MultiLine-Record
Fängt das letzte Feld mit einem Feldbegrenzer und einem Textbegrenzer an? - Klare Sache, ist ein MutliLine-Record.

Hier mal der Code:


        private bool IsMultiLineColumn(string line, TextSourceDescription description)
        {
            var reversedLine = string.Join("", line.Reverse());
            var beginsWithFieldAndTextSeparator = false;

            //If the last character of the current row is a text separator, the record isn't multiline
            if (reversedLine.First() == description.FieldsEndWith)
            {
                return false;
            }

            for (var index = 0; index < line.Length; index++)
            {
                //Checks if the current record ends with a text separator
                if (reversedLine[index] != description.FieldsBeginWith)
                {
                    continue;
                }

                //If the current field is the first field of this record, we're not able to check, if the first character is a field separator, 
                //because the first field does not contains a field separator at the first position
                if (index >= reversedLine.Length)
                {
                    beginsWithFieldAndTextSeparator = true;
                }
                else
                {
                    //Checks if the field doesn't start with a text separator and a field separator
                    if (!description.FieldSeparators.Any(fieldSeparator => reversedLine.Length >= index + fieldSeparator.Length 
                        && reversedLine.IndexOf(fieldSeparator, StringComparison.InvariantCulture) == index + 1))
                    {
                        continue;
                    }

                    beginsWithFieldAndTextSeparator = true;
                    break;
                }
            }

            return beginsWithFieldAndTextSeparator;
        }

185 Beiträge seit 2005
vor 8 Jahren

Schau dir mal den TextFieldParser an.

Du must dazu Microsoft.VisualBasic referenzieren.

3.003 Beiträge seit 2006
vor 8 Jahren

Für ein Parsing bietet sich das Visitor-Pattern an. Damit kannst du die einzelnen Prüfungen wesentlich besser voneinander trennen und ggf. verändern (zB wenn die Anforderungen sich ändern).

Wenn es nicht so weit gehen soll, pragmatische Lösung:

  • vor dem Parsen im geparsten Text alle Zeilenumbrüche durch eine eindeutige Zeichenkette ersetzen
  • Parsen
  • in allen Feldern die eindeutige Zeichenkette wieder zurückersetzen

Mit anderen Worten: das Problem beseitigen und hinterher wieder einsetzen.

Geht per regex schnell und zuverlässig (so lange, bis die "eindeutige" Zeichenkette als Text in einem Datenfeld vorkommt, schon klar 😁 )

Und da wir im Review-Forum sind: ich zähle in der von dir geposteten Methode 2 Aussprungpunkte, eine Schleife mit acht Möglichkeiten der Auswertung und drei Spunganweisungen auf insgesamt 4 Codezeilen (Verzweigungen ausgenommen). Das ist ein paar Größenordnungen zu komplex für guten, wartbaren Code und führt mich zu der Vermutung, dass der Rest des Projektes dabei ist, sich in ein Spaghettimonster zu verwandeln.

LaTino

"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)