Kommazeichengetrennte CSV einlesen und schreiben mit Komma in Values

  • VB.NET

Es gibt 6 Antworten in diesem Thema. Der letzte Beitrag () ist von xored.

    Kommazeichengetrennte CSV einlesen und schreiben mit Komma in Values

    Guten Morgen,

    ich habe mir ein kleines Tool gebaut um mir CSV-Listen für Shopify zu bauen, um Artikel per Knopfdruck anzulegen, statt mühsam die CSV selbst auszufüllen.
    Das Problem ist, dass Shopify ausschließlich Kommazeichengetrennte CSV's akzeptiert. Die Einlesemethode in das DataGridView via String.Split hat mir nur Probleme bereitet,
    wenn das Splitzeichen ein Komma war.

    Nun habe ich den TextFieldParser gefunden, dieser Lädt meine CSV ohne Probleme in das DataGridView (bzw. DataTable), sodass alles korrekt formatiert ist.
    Jetzt mein Problem: Eine Spalte ist die Produktbeschreibung. Dort kommen sehr wohl auch "," vor. Sobald das passiert wird meine CSV nicht mehr korrekt eingelesen (Felder verschieben sich etc.).

    Hier mal der Code zum Einlesen, welchen ich gefunden hatte:

    VB.NET-Quellcode

    1. ​Dim csvData As DataTable = New DataTable()
    2. Public Function GetDataTabletFromCSVFile(ByVal csv_file_path As String) As DataTable
    3. Try
    4. Using csvReader As TextFieldParser = New TextFieldParser(csv_file_path)
    5. csvReader.SetDelimiters(New String() {","})
    6. csvReader.HasFieldsEnclosedInQuotes = True
    7. Dim colFields As String() = csvReader.ReadFields()
    8. For Each column As String In colFields
    9. Dim datecolumn As DataColumn = New DataColumn(column)
    10. datecolumn.AllowDBNull = True
    11. csvData.Columns.Add(datecolumn)
    12. Next
    13. While Not csvReader.EndOfData
    14. Dim fieldData As String() = csvReader.ReadFields()
    15. For i As Integer = 0 To fieldData.Length - 1
    16. If fieldData(i) = "" Then
    17. fieldData(i) = Nothing
    18. End If
    19. Next
    20. csvData.Rows.Add(fieldData)
    21. End While
    22. End Using
    23. Catch ex As Exception
    24. MessageBox.Show(ex.Message)
    25. End Try
    26. Return csvData
    27. End Function


    Ich habe schon probiert um alle Kommazeichen ein " oder ' zu packen, leider ohne Erfolg. Wie escape ich diese nun korrekt?
    Hat jemand eine Idee? Ich könnte diese natürlich auch durch " " ersetzen, aber das ist ja nicht der Sinn dabei.

    Nachtrag:
    Das mit dem Einlesen hab ich behoben:

    VB.NET-Quellcode

    1. If Not IsDBNull(fieldData(i)) And Not (String.IsNullOrEmpty(fieldData(i))) Then
    2. fieldData(i) = fieldData(i).Replace(",", "','")
    3. End If



    Und nach dem Einlesen dann:

    VB.NET-Quellcode

    1. For rows As Integer = 0 To DataGridView1.Rows.Count - 2
    2. If Not IsDBNull(DataGridView1.Rows(rows).Cells(2).Value) Then
    3. DataGridView1.Rows(rows).Cells(2).Value = DataGridView1.Rows(rows).Cells(2).Value.replace("','", ",")
    4. End If
    5. Next


    Das klappt, alle Werte sind da, wo sie hingehören und die Kommazeichen sind auch richtig.

    Jetzt hab ich das gleiche Problem rückwärts beim Schreiben.
    Bisher nutze ich folgende Methode:

    VB.NET-Quellcode

    1. Private Function dtTableToCSV(dt As DataTable, filename As String, Optional headers As Boolean = True, Optional delim As String = ",")
    2. Dim txt As String
    3. Dim fileloc As String = filename
    4. If File.Exists(fileloc) Then
    5. File.Delete(fileloc)
    6. End If
    7. Dim n = 0
    8. If headers = True Then
    9. For Each column As DataColumn In dt.Columns
    10. If n = 0 Then
    11. txt += column.ColumnName
    12. Else
    13. txt += delim + column.ColumnName
    14. End If
    15. n += 1
    16. Next
    17. End If
    18. txt += vbCrLf
    19. n = 0
    20. For Each row As DataRow In dt.Rows
    21. Dim line As String = ""
    22. For Each column As DataColumn In dt.Columns
    23. line += delim & row(column.ColumnName).ToString()
    24. Next
    25. If dt.Rows.Count - 1 = n Then
    26. txt += line.Substring(1)
    27. Else
    28. txt += line.Substring(1) & vbCrLf
    29. End If
    30. n += 1
    31. Next
    32. Using sw As StreamWriter = New StreamWriter(fileloc)
    33. sw.Write(txt)
    34. End Using
    35. dt.Dispose()
    36. Return fileloc
    37. End Function



    Dort muss ich nun nur noch schaffen, dass das Kommazeichen auch richtig exportiert wird.

    Probiert habe ich folgendes:

    VB.NET-Quellcode

    1. ​Using sw As StreamWriter = New StreamWriter(fileloc)
    2. sw.Write(txt.Replace("','", """" & "," & """"))
    3. End Using


    Das zerschießt aber trotzdem die CSV, wenn ich diese dann in Excel o.Ä. einlese. Ab dem Punkt wo das Komma auftritt, ist alles verschoben.
    Hat jemand eine Idee, wie ich die CSV nun mit Kommata schreiben könnte?

    Vielen Dank für Tipps :)
    Grüße,
    xored


    Meine Website:
    www.renebischof.de

    Meine erste App (Android):
    PartyPalooza
    das csv-Format kannste zB auf Wikipedia nachlesen.
    paar Tips:
    Verwende den Microsoft.Visualbasic.TextFieldParser zum Einlesen - der machts richtig. Spar dir die Versuche, es selbst zu versuchen.

    den Writer musste leider selber schreiben.
    Grundsätzlich muss jeder Wert analysiert werden, ob er das Trennzeichen enthält, oder den DoubleQuote ".
    Enthaltene DoubleQuotes müssen escaped werden, einfach, indem du sie verdoppelst.

    Ist das Trennzeichen vorhanden, muss der ganze Wert gequotet werden - mit DoubleQuote.
    Vereinfachend kannste alle Werte auch immer quoten wie inopiae sagt - das ergibt ungewöhnliches csv, aber nicht verboten.

    Nochmal: Verwende den TextFieldParser zum Lesen. Dann kannste auch einen regelkonformen Writer entwickeln, und dann passt das zusammen.
    Habich auch Tut dazu im Tut-Bereich: Csv importieren
    Danke euch.

    @ErfinderDesRades
    Den TextFieldParser benutze ich ja schon, einlesen ist auch nicht das Problem. Das Problem liegt glaube ich am Escapen bei mir.
    Das mit den DoubleQuotes ist ein guter Tipp, werde es nochmal probieren. Hatte schon beim Schreiben um jeden Wert ein " eingefügt, aber die CSV-Datei war trotzdem durcheinander..
    Ich teste mal rum.

    Edit, habe um jede Value ein " gesetzt. Nach dem Export ist die CSV-Datei trotzdem "zerstört"..
    Im Datagridview sieht es richtig aus:



    VB.NET-Quellcode

    1. If Not IsDBNull(fieldData(i)) And Not (String.IsNullOrEmpty(fieldData(i))) Then
    2. fieldData(i) = String.Format("""{0}""", fieldData(i))
    3. End If


    Ich werde jetzt mal die CSV-Datei von Shopify in Notepad++ analysieren und schauen, wo die Unterschiede zu meiner CSV liegen.


    Meine Website:
    www.renebischof.de

    Meine erste App (Android):
    PartyPalooza

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „xored“ ()

    xored schrieb:

    Den TextFieldParser benutze ich ja schon
    uih - da habich ja malwwieder megaschlampig gelesen, sorry.

    jdfs. sehe ich noch nicht, wie du DoubleQuotes maskierst.

    Jo, ansonsten musste tatsächlich mal die Shopify-csvs angugge. Ich könnte mir vorstellen, da werden Datumse, Zeiten und Zahlen in einem bestimmten Format erwartet, welche du aber anders formatiert bereitstellst.
    Wenn das so ist, arbeite mit der Culture - höchstwahrscheinlich erwartet Shopify CultureInfo.Invariant.
    Dein Writer writet aber einfach mit value.ToString, und das wiederum verwendet die installierte Kultur, vmtl. Deutsch.
    Evtl. musste auch die Datentypen der DataColumns an Shopifys Bedürfnissen anpassen.
    Verwende CultureInfo - Versuch nicht, mit String.Replace iwelche Formatierungs-Details umzubiegen - dabei übersieht man andere Details, die dir dann später auf die Füsse fallen.
    Danke für eure Tipps. Habe es nun mit CsvHelper probiert und es klappt. Hätte es zwar lieber selbst ohne dritte Packets geschafft, aber man muss ja nicht das Rad neu erfinden,
    @ErfinderDesRades hat das ja schon :D

    Falls jemand in Zukunft das gleiche Problem haben sollte, hier mein zusammengeschusteter Code:

    VB.NET-Quellcode

    1. Public Sub Export(dt As DataTable, Path As String)
    2. Dim configuration = New CsvConfiguration(CultureInfo.InvariantCulture) With {
    3. .Encoding = Encoding.UTF8,
    4. .Delimiter = ","
    5. }
    6. Using textWriter = File.CreateText(Path)
    7. Using csv = New CsvWriter(textWriter, configuration)
    8. For Each column As DataColumn In dt.Columns
    9. csv.WriteField(column.ColumnName)
    10. Next
    11. csv.NextRecord()
    12. For Each row As DataRow In dt.Rows
    13. For i = 0 To dt.Columns.Count - 1
    14. csv.WriteField(row(i))
    15. Next
    16. csv.NextRecord()
    17. Next
    18. End Using
    19. End Using
    20. End Sub



    Grüße,
    xored


    Meine Website:
    www.renebischof.de

    Meine erste App (Android):
    PartyPalooza