wake-up-neo.com

Regex, um eine CSV zu teilen

Ich weiß, dass (oder ähnlich) oft gefragt wurde, aber nachdem ich zahlreiche Möglichkeiten ausprobiert hatte, konnte ich keinen Regex finden, der zu 100% funktioniert.

Ich habe eine CSV-Datei und versuche, sie in ein Array aufzuteilen, dabei treten jedoch zwei Probleme auf: Anführungszeichen und leere Elemente.

Der CSV sieht so aus:

123,2.99,AMO024,Title,"Description, more info",,123987564

Die Regex, die ich versucht habe, ist:

thisLine.split(/,(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))/)

Das einzige Problem ist, dass das 5. Element in meinem Ausgabearray als 123987564 und nicht als leerer String ausgegeben wird.

35
Code Ninja

Beschreibung

Anstatt einen Split zu verwenden, denke ich, wäre es einfacher, einfach ein Match auszuführen und alle gefundenen Matches zu verarbeiten.

Dieser Ausdruck wird:

  • teilen Sie Ihren Beispieltext auf die Kommagrenze auf
  • verarbeitet leere Werte
  • ignoriert doppelte Anführungszeichen, sofern doppelte Anführungszeichen nicht geschachtelt werden
  • schneidet das begrenzende Komma vom zurückgegebenen Wert ab
  • schneidet umgebende Anführungszeichen aus dem zurückgegebenen Wert ab

Regex: (?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)

enter image description here

Beispiel

Beispieltext

123,2.99,AMO024,Title,"Description, more info",,123987564

ASP Beispiel mit dem Nicht-Java-Ausdruck

Set regEx = New RegExp
regEx.Global = True
regEx.IgnoreCase = True
regEx.MultiLine = True
sourcestring = "your source string"
regEx.Pattern = "(?:^|,)(?=[^""]|("")?)""?((?(1)[^""]*|[^,""]*))""?(?=,|$)"
Set Matches = regEx.Execute(sourcestring)
  For z = 0 to Matches.Count-1
    results = results & "Matches(" & z & ") = " & chr(34) & Server.HTMLEncode(Matches(z)) & chr(34) & chr(13)
    For zz = 0 to Matches(z).SubMatches.Count-1
      results = results & "Matches(" & z & ").SubMatches(" & zz & ") = " & chr(34) & Server.HTMLEncode(Matches(z).SubMatches(zz)) & chr(34) & chr(13)
    next
    results=Left(results,Len(results)-1) & chr(13)
  next
Response.Write "<pre>" & results

Übereinstimmungen mit dem Nicht-Java-Ausdruck

Gruppe 0 erhält den gesamten Teilstring, der das Komma enthält
Gruppe 1 erhält das Zitat, wenn es verwendet wird
Gruppe 2 erhält den Wert ohne Komma

[0][0] = 123
[0][1] = 
[0][2] = 123

[1][0] = ,2.99
[1][1] = 
[1][2] = 2.99

[2][0] = ,AMO024
[2][1] = 
[2][2] = AMO024

[3][0] = ,Title
[3][1] = 
[3][2] = Title

[4][0] = ,"Description, more info"
[4][1] = "
[4][2] = Description, more info

[5][0] = ,
[5][1] = 
[5][2] = 

[6][0] = ,123987564
[6][1] = 
[6][2] = 123987564
44
Ro Yo Mi

Ich habe dies vor einigen Monaten für ein Projekt erstellt.

 ".+?"|[^"]+?(?=,)|(?<=,)[^"]+

Regular expression visualization

Es funktioniert in C # und das Debuggex war glücklich, als ich Python und PCRE auswählte. Javascript erkennt diese Form von Proceeded By ? <= ... nicht.

Für Ihre Werte werden Übereinstimmungen erstellt

123
,2.99
,AMO024
,Title
"Description, more info"
,
,123987564

Beachten Sie, dass alles in Anführungszeichen kein führendes Komma enthält, aber der Versuch, ein führendes Komma zu finden, war für den Anwendungsfall für leere Werte erforderlich. Trimmen Sie anschließend die Werte nach Bedarf.

Ich benutze RegexHero.Net , um mein Regex zu testen.

9
scott.smart

Ich brauchte diese Antwort auch, aber ich fand die Antworten, obwohl sie informativ waren, etwas schwer zu folgen und für andere Sprachen zu replizieren. Hier ist der einfachste Ausdruck, den ich für eine einzelne Spalte der CSV-Zeile gefunden habe. Ich spalte nicht Ich baue einen Regex, der mit einer Spalte aus dem CSV übereinstimmt, so dass ich die Linie nicht spalte:

("([^"]*)"|[^,]*)(,|$)

Dies entspricht einer einzelnen Spalte aus der CSV-Zeile. Der erste Teil "([^"]*)" des Ausdrucks soll mit einem Eintrag in Anführungszeichen übereinstimmen, der zweite Teil [^,]* soll mit einem nicht in Anführungszeichen enthaltenen Eintrag übereinstimmen. Dann entweder gefolgt von einem , oder dem Zeilenende $.

Und das begleitende Debuggex, um den Ausdruck zu testen.

https://www.debuggex.com/r/s4z_Qi2gZiyzpAhx

6
chubbsondubs

Ich habe ein bisschen daran gearbeitet und kam zu dieser Lösung:

(?:,|\n|^)("(?:(?:"")*[^"]*)*"|[^",\n]*|(?:\n|$))

Probier es hier aus!

Diese Lösung verarbeitet "nette" CSV-Daten wie

"a","b",c,"d",e,f,,"g"

0: "a"
1: "b"
2: c
3: "d"
4: e
5: f
6:
7: "g"

und hässlichere Dinge mögen

"""test"" one",test' two,"""test"" 'three'","""test 'four'"""

0: """test"" one"
1: test' two
2: """test"" 'three'"
3: """test 'four'"""

Hier ist eine Erklärung, wie es funktioniert :

(?:,|\n|^)      # all values must start at the beginning of the file,  
                #   the end of the previous line, or at a comma  
(               # single capture group for ease of use; CSV can be either...  
  "             # ...(A) a double quoted string, beginning with a double quote (")  
    (?:         #        character, containing any number (0+) of  
      (?:"")*   #          escaped double quotes (""), or  
      [^"]*     #          non-double quote characters  
    )*          #        in any order and any number of times  
  "             #        and ending with a double quote character  

  |             # ...or (B) a non-quoted value  

  [^",\n]*      # containing any number of characters which are not  
                # double quotes ("), commas (,), or newlines (\n)  

  |             # ...or (C) a single newline or end-of-file character,  
                #           used to capture empty values at the end of  
  (?:\n|$)      #           the file or at the ends of lines  
)
6
awwsmm

Ich bin spät dran, aber Folgendes ist der reguläre Ausdruck, den ich verwende:

(?:,"|^")(""|[\w\W]*?)(?=",|"$)|(?:,(?!")|^(?!"))([^,]*?)(?=$|,)|(\r\n|\n)

Dieses Muster hat drei Erfassungsgruppen:

  1. Inhalt einer zitierten Zelle
  2. Inhalt einer Zelle ohne Anführungszeichen
  3. Eine neue Zeile

Dieses Muster behandelt Folgendes:

  • Normaler Zellinhalt ohne Besonderheiten: one, 2, three
  • Zelle, die ein doppeltes Anführungszeichen enthält ("ist in" "Escapezeichen"): kein Anführungszeichen, "" ein "" zitiertes "" Ding ", end
  • Zelle enthält ein Zeilenvorschubzeichen: eins, zwei\ndrei, vier
  • Normale Zelleninhalte, die ein internes Zitat haben: eins, zwei "drei, vier
  • Zelle enthält Anführungszeichen gefolgt von Komma: Eins, "zwei" "drei" ", vier", fünf

Sehen Sie dieses Muster in Verwendung.

Wenn Sie mit benannten Gruppen und Lookbehinds eine leistungsfähigere Variante von Regex verwenden, bevorzuge ich Folgendes:

(?<quoted>(?<=,"|^")(?:""|[\w\W]*?)*(?=",|"$))|(?<normal>(?<=,(?!")|^(?!"))[^,]*?(?=(?<!")$|(?<!"),))|(?<eol>\r\n|\n)

Sehen Sie dieses Muster in Verwendung.

Bearbeiten

(?:^"|,")(""|[\w\W]*?)(?=",|"$)|(?:^(?!")|,(?!"))([^,]*?)(?=$|,)|(\r\n|\n)

Dieses leicht modifizierte Muster behandelt Zeilen, in denen die erste Spalte leer ist, solange Sie kein Javascript verwenden. Aus irgendeinem Grund lässt Javascript die zweite Spalte dieses Musters weg. Ich konnte diesen Edge-Fall nicht richtig bearbeiten.

5
bublebboy

Ich persönlich habe viele RegEx-Ausdrücke ausprobiert, ohne für jeden Fall den passenden Ausdruck gefunden zu haben.

Ich denke, dass reguläre Ausdrücke schwer zu konfigurieren sind, um alle Fälle richtig abzugleichen. Obwohl der Namespace nur wenigen Personen gefällt (und ich war ein Teil davon), schlage ich etwas vor, das Teil des .NET-Frameworks ist, und gebe mir in allen Fällen zu allen Zeiten richtige Ergebnisse (meistens werden alle Anführungszeichen sehr gut verarbeitet):

Microsoft.VisualBasic.FileIO.TextFieldParser

Fand es hier: StackOverflow

Verwendungsbeispiel:

TextReader textReader = new StringReader(simBaseCaseScenario.GetSimStudy().Study.FilesToDeleteWhenComplete);
Microsoft.VisualBasic.FileIO.TextFieldParser textFieldParser = new TextFieldParser(textReader);
textFieldParser.SetDelimiters(new string[] { ";" });
string[] fields = textFieldParser.ReadFields();
foreach (string path in fields)
{
    ...

Ich hoffe es könnte helfen.

3
Eric Ouellet

Die Verwendung von JScript für klassische ASP - Seiten hat den Vorteil, dass Sie eine der vielen Bibliotheken verwenden können, die für JavaScript geschrieben wurden.

Wie dieser hier: https://github.com/gkindel/CSV-JS . Laden Sie es herunter, fügen Sie es in Ihre ASP -Seite ein, analysieren Sie CSV damit.

<%@ language="javascript" %>

<script language="javascript" runat="server" src="scripts/csv.js"></script>
<script language="javascript" runat="server">

var text = '123,2.99,AMO024,Title,"Description, more info",,123987564',
    rows = CSV.parse(line);

    Response.Write(rows[0][4]);
</script>
3
Tomalak

In Java arbeitet dieses Muster ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))" fast für mich:

String text = "\",\",\",,\",,\",asdasd a,sd s,ds ds,dasda,sds,ds,\"";
String regex = ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))";
Pattern p = Pattern.compile(regex);
String[] split = p.split(text);
for(String s:split) {
    System.out.println(s);
}

ausgabe:

","
",a,,"

",asdasd a,sd s,ds ds,dasda,sds,ds,"

Nachteil: funktioniert nicht, wenn die Spalte eine ungerade Anzahl von Anführungszeichen hat :(

3
marioosh

Aaaund noch eine Antwort hier. :) Da ich die anderen nicht ziemlich zum Laufen bringen konnte.

Meine Lösung behandelt sowohl mit Escapezeichen versehene Anführungszeichen (doppelte Vorkommen) als auch Trennzeichen in der Übereinstimmung.

Beachten Sie, dass ich mit ' statt " verglichen habe, da dies mein Szenario war, aber ersetzen Sie sie einfach im Muster, um denselben Effekt zu erzielen.

Hier geht es (denken Sie daran, das Kennzeichen "Whorepace ignorieren" /x zu verwenden, wenn Sie die kommentierte Version verwenden):

# Only include if previous char was start of string or delimiter
(?<=^|,)
(?:
  # 1st option: empty quoted string (,'',)
  '{2}
  |
  # 2nd option: nothing (,,)
  (?:)
  |
  # 3rd option: all but quoted strings (,123,)
  # (included linebreaks to allow multiline matching)
  [^,'\r\n]+
  |
  # 4th option: quoted strings (,'123''321',)
  # start pling
  ' 
    (?:
      # double quote
      '{2}
      |
      # or anything but quotes
      [^']+
    # at least one occurance - greedy
    )+
  # end pling
  '
)
# Only include if next char is delimiter or end of string
(?=,|$)

Einzeilige Version:

(?<=^|,)(?:'{2}|(?:)|[^,'\r\n]+|'(?:'{2}|[^']+)+')(?=,|$)

Regular expression visualization (if it works, debux has issues right now it seems - else follow the next link)

Debuggex Demo

regex101 Beispiel

1
Johny Skovdal

Ich verwende dieses, es funktioniert mit einem Koma-Trennzeichen und einem doppelten Anführungszeichen.

/(?<=^|,)(\"(?:[^"]+|"")*\"|[^,]*)(?:$|,)/g

Eine weitere Antwort mit einigen zusätzlichen Funktionen wie der Unterstützung von Werten in Anführungszeichen, die Anführungszeichen und CR/LF-Zeichen enthalten (einzelne Werte, die mehrere Zeilen umfassen).

ANMERKUNG: Obwohl die unten stehende Lösung wahrscheinlich für andere Regex-Engines angepasst werden kann, erfordert dies die Verwendung von as-is Ihre Regex-Engine behandelt mehrere benannte Erfassungsgruppen mit demselben Namen als eine einzelne Erfassungsgruppe. (.NET macht dies standardmäßig)


Wenn mehrere Zeilen/Datensätze einer CSV-Datei/eines CSV-Streams (Matching RFC-Standard 418 ) an den regulären Ausdruck darunter übergeben werden, wird für jede nicht leere Zeile/jeden Datensatz eine Übereinstimmung zurückgegeben. Jede Übereinstimmung enthält eine Erfassungsgruppe mit dem Namen Value, die die erfassten Werte in dieser Zeile/Datensatz enthält (und möglicherweise eine Erfassungsgruppe OpenValue, wenn am Ende von ein offenes Zitat vorhanden war die Zeile/Aufzeichnung).

Hier ist das kommentierte Muster (teste es auf Regexstorm.net ):

(?<=\r|\n|^)(?!\r|\n|$)                       // Records start at the beginning of line (line must not be empty)
(?:                                           // Group for each value and a following comma or end of line (EOL) - required for quantifier (+?)
  (?:                                         // Group for matching one of the value formats before a comma or EOL
    "(?<Value>(?:[^"]|"")*)"|                 // Quoted value -or-
    (?<Value>(?!")[^,\r\n]+)|                 // Unquoted value -or-
    "(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)|   // Open ended quoted value -or-
    (?<Value>)                                // Empty value before comma (before EOL is excluded by "+?" quantifier later)
  )
  (?:,|(?=\r|\n|$))                           // The value format matched must be followed by a comma or EOL
)+?                                           // Quantifier to match one or more values (non-greedy/as few as possible to prevent infinite empty values)
(?:(?<=,)(?<Value>))?                         // If the group of values above ended in a comma then add an empty value to the group of matched values
(?:\r\n|\r|\n|$)                              // Records end at EOL


Hier ist das Rohmuster ohne alle Kommentare oder Leerzeichen.

(?<=\r|\n|^)(?!\r|\n|$)(?:(?:"(?<Value>(?:[^"]|"")*)"|(?<Value>(?!")[^,\r\n]+)|"(?<OpenValue>(?:[^"]|"")*)(?=\r|\n|$)|(?<Value>))(?:,|(?=\r|\n|$)))+?(?:(?<=,)(?<Value>))?(?:\r\n|\r|\n|$)


Hier ist eine Visualisierung von Debuggex.com (Capture-Gruppen, die der Klarheit halber benannt wurden): Debuggex.com visualization

Beispiele zur Verwendung des Regex-Musters finden Sie in meiner Antwort auf eine ähnliche Frage hier oder auf C # -Pad hier oder hier .

1
David Woodward
,?\s*'.+?'|,?\s*".+?"|[^"']+?(?=,)|[^"']+  

Diese Regex funktioniert mit einfachen und doppelten Anführungszeichen und auch für ein Zitat innerhalb eines anderen!

0
zerocool

Wenn Sie wissen, dass Sie kein leeres Feld (,) haben, funktioniert dieser Ausdruck gut:

("[^"]*"|[^,]+)

Wie im folgenden Beispiel ...

Set rx = new RegExp
rx.Pattern = "(""[^""]*""|[^,]+)"
rx.Global = True
Set col = rx.Execute(sText)
For n = 0 to col.Count - 1
    if n > 0 Then s = s & vbCrLf
    s = s & col(n)
Next

Wenn Sie jedoch ein leeres Feld erwarten und Ihr Text relativ klein ist, sollten Sie die leeren Felder vor der Analyse durch ein Leerzeichen ersetzen, um sicherzustellen, dass sie erfasst werden. Zum Beispiel...

...
Set col = rx.Execute(Replace(sText, ",,", ", ,"))
...

Wenn Sie die Integrität der Felder beibehalten möchten, können Sie die Kommas wiederherstellen und Leerzeichen innerhalb der Schleife testen. Dies ist möglicherweise nicht die effizienteste Methode, aber die Arbeit wird erledigt. 

0
k rey

Wenn ich den Regex ausprobiere, der von @chubbsondubs auf http://regex101.com mit dem 'g'-Flag gepostet wird, gibt es Übereinstimmungen, die nur', 'oder einen leeren String enthalten. 
(?:"([^"]*)"|([^,]*))(?:[,]) 
Ich kann die Teile des CSV zusammenbringen (einschließlich der zitierten Teile). (Die Zeile muss mit einem ',' abgeschlossen sein, andernfalls wird der letzte Teil nicht erkannt.) 
https://regex101.com/r/dF9kQ8/4
Wenn der CSV so aussieht:
"",huhu,"hel lo",world, 
Es gibt 4 Treffer:
'' 
'huhu' 
'Hallo' 
'Welt' 

0
deemon

Dies entspricht alles, was ich in c # brauche:

(?<=(^|,)(?<quote>"?))([^"]|(""))*?(?=\<quote>(?=,|$))
  • zitate streifen
  • lässt neue Zeilen
  • erlaubt doppelte Anführungszeichen in der Zeichenfolge
  • lässt Kommas in der angegebenen Zeichenfolge
0
Ilya Rudenko

Ich hatte eine ähnliche Notwendigkeit, CSV-Werte von SQL-Einfügeanweisungen aufzuteilen. 

In meinem Fall konnte ich davon ausgehen, dass Strings in einzelne Anführungszeichen gesetzt wurden und Zahlen nicht.

csv.split(/,((?=')|(?=\d))/g).filter(function(x) { return x !== '';});

Aus irgendeinem offensichtlichen Grund führt diese Regex zu leeren Ergebnissen. Ich könnte diese ignorieren, da leere Werte in meinen Daten als ...,'',... und nicht als ...,,... dargestellt wurden.

0
peter