Wie kann ich eine CSV-Zeichenfolge mit Javascript analysieren, das Komma in Daten enthält?

93

Ich habe die folgende Art von Zeichenfolge

var string = "'string, duppi, du', 23, lala"

Ich möchte die Zeichenfolge bei jedem Komma in ein Array aufteilen, aber nur die Kommas außerhalb der einfachen Anführungszeichen.

Ich kann den richtigen regulären Ausdruck für die Trennung nicht herausfinden ...

string.split(/,/)

wird mir geben

["'string", " duppi", " du'", " 23", " lala"]

aber das Ergebnis sollte sein:

["string, duppi, du", "23", "lala"]

Gibt es eine browserübergreifende Lösung?

Hans
quelle
Ist es immer in einfachen Anführungszeichen? Gibt es jemals ein einfaches Anführungszeichen in einer Zeichenfolge in Anführungszeichen? Wenn ja, wie wird es entkommen (Backslash, verdoppelt)?
Phrogz
Was ist, wenn die Anführungszeichen wie in JavaScript und HTML / XML-Code vollständig zwischen doppelten und einfachen Anführungszeichen austauschbar sind? Wenn ja, dann erfordert dies eine umfangreichere Analyseoperation als CSV.
Austincheney
Eigentlich ja, es könnte ein einziges Zitat im Inneren geben, mit Backslash zu entkommen wäre in Ordnung.
Hans
Kann ein Wert eine Zeichenfolge in doppelten Anführungszeichen sein?
Ridgerunner
1
Papa Parse macht einen guten Job. Parsen einer lokalen CSV-Datei mit JavaScript und Papa Parse: joyofdata.de/blog/…
Raffael

Antworten:

214

Haftungsausschluss

01.12.2014 Update: Die folgende Antwort funktioniert nur für ein ganz bestimmtes CSV-Format. Wie DG in den Kommentaren korrekt ausgeführt hat, passt diese Lösung NICHT zur RFC 4180-Definition von CSV und auch NICHT zum MS Excel-Format. Diese Lösung zeigt einfach, wie eine (nicht standardmäßige) CSV-Eingabezeile analysiert werden kann, die eine Mischung von Zeichenfolgentypen enthält, wobei die Zeichenfolgen maskierte Anführungszeichen und Kommas enthalten können.

Eine nicht standardmäßige CSV-Lösung

Wie austincheney richtig hervorhebt, müssen Sie die Zeichenfolge wirklich von Anfang bis Ende analysieren, wenn Sie Zeichenfolgen in Anführungszeichen, die möglicherweise maskierte Zeichen enthalten, richtig verarbeiten möchten. Außerdem definiert das OP nicht klar, was eine "CSV-Zeichenfolge" wirklich ist. Zuerst müssen wir definieren, was eine gültige CSV-Zeichenfolge und ihre einzelnen Werte ausmacht.

Gegeben: "CSV String" Definition

Für die Zwecke dieser Diskussion besteht eine "CSV-Zeichenfolge" aus null oder mehr Werten, wobei mehrere Werte durch ein Komma getrennt sind. Jeder Wert kann bestehen aus:

  1. Eine Zeichenfolge in doppelten Anführungszeichen. (Kann einfache Anführungszeichen enthalten.)
  2. Eine einfache Zeichenfolge in Anführungszeichen. (Kann doppelte Anführungszeichen enthalten.)
  3. Eine Zeichenfolge ohne Anführungszeichen. (Darf KEINE Anführungszeichen, Kommas oder Backslashes enthalten.)
  4. Ein leerer Wert. (Ein Leerzeichenwert wird als leer betrachtet.)

Regeln / Hinweise:

  • Anführungszeichen können Kommas enthalten.
  • Zitierte Werte können Escape-alles enthalten, z 'that\'s cool'.
  • Werte, die Anführungszeichen, Kommas oder Backslashes enthalten, müssen in Anführungszeichen gesetzt werden.
  • Werte, die führende oder nachfolgende Leerzeichen enthalten, müssen in Anführungszeichen gesetzt werden.
  • Der Backslash wird von allen entfernt: \'in einfachen Anführungszeichen.
  • Der Backslash wird von allen entfernt: \"in doppelten Anführungszeichen.
  • Nicht in Anführungszeichen gesetzte Zeichenfolgen werden von führenden und nachfolgenden Leerzeichen abgeschnitten.
  • Das Komma-Trennzeichen kann benachbarte Leerzeichen enthalten (die ignoriert werden).

Finden:

Eine JavaScript-Funktion, die eine gültige CSV-Zeichenfolge (wie oben definiert) in ein Array von Zeichenfolgenwerten konvertiert.

Lösung:

Die von dieser Lösung verwendeten regulären Ausdrücke sind komplex. Und (IMHO) alle nicht trivialen regulären Ausdrücke sollten im Freiraummodus mit vielen Kommentaren und Einrückungen dargestellt werden. Leider erlaubt JavaScript keinen Freiraummodus. Daher werden die von dieser Lösung implementierten regulären Ausdrücke zunächst in nativer Regex-Syntax dargestellt (ausgedrückt mit Pythons praktischer Syntax: r'''...'''Raw-Multi-Line-String).

Hier ist zunächst ein regulärer Ausdruck, der bestätigt, dass eine CVS-Zeichenfolge die oben genannten Anforderungen erfüllt:

Regex zur Validierung einer "CSV-Zeichenfolge":

re_valid = r"""
# Validate a CSV string having single, double or un-quoted values.
^                                   # Anchor to start of string.
\s*                                 # Allow whitespace before value.
(?:                                 # Group for value alternatives.
  '[^'\\]*(?:\\[\S\s][^'\\]*)*'     # Either Single quoted string,
| "[^"\\]*(?:\\[\S\s][^"\\]*)*"     # or Double quoted string,
| [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*    # or Non-comma, non-quote stuff.
)                                   # End group of value alternatives.
\s*                                 # Allow whitespace after value.
(?:                                 # Zero or more additional values
  ,                                 # Values separated by a comma.
  \s*                               # Allow whitespace before value.
  (?:                               # Group for value alternatives.
    '[^'\\]*(?:\\[\S\s][^'\\]*)*'   # Either Single quoted string,
  | "[^"\\]*(?:\\[\S\s][^"\\]*)*"   # or Double quoted string,
  | [^,'"\s\\]*(?:\s+[^,'"\s\\]+)*  # or Non-comma, non-quote stuff.
  )                                 # End group of value alternatives.
  \s*                               # Allow whitespace after value.
)*                                  # Zero or more additional values
$                                   # Anchor to end of string.
"""

Wenn eine Zeichenfolge mit dem obigen regulären Ausdruck übereinstimmt, ist diese Zeichenfolge eine gültige CSV-Zeichenfolge (gemäß den zuvor angegebenen Regeln) und kann mit dem folgenden regulären Ausdruck analysiert werden. Der folgende reguläre Ausdruck wird dann verwendet, um einen Wert aus der CSV-Zeichenfolge abzugleichen. Es wird wiederholt angewendet, bis keine Übereinstimmungen mehr gefunden werden (und alle Werte analysiert wurden).

Regex, um einen Wert aus einer gültigen CSV-Zeichenfolge zu analysieren:

re_value = r"""
# Match one value in valid CSV string.
(?!\s*$)                            # Don't match empty last value.
\s*                                 # Strip whitespace before value.
(?:                                 # Group for value alternatives.
  '([^'\\]*(?:\\[\S\s][^'\\]*)*)'   # Either $1: Single quoted string,
| "([^"\\]*(?:\\[\S\s][^"\\]*)*)"   # or $2: Double quoted string,
| ([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)  # or $3: Non-comma, non-quote stuff.
)                                   # End group of value alternatives.
\s*                                 # Strip whitespace after value.
(?:,|$)                             # Field ends on comma or EOS.
"""

Beachten Sie, dass es einen Sonderfallwert gibt, mit dem dieser reguläre Ausdruck nicht übereinstimmt - den allerletzten Wert, wenn dieser Wert leer ist. Dieser spezielle Fall "leerer letzter Wert" wird von der folgenden js-Funktion getestet und behandelt.

JavaScript-Funktion zum Parsen von CSV-Zeichenfolgen:

// Return array of string values, or NULL if CSV string not well formed.
function CSVtoArray(text) {
    var re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/;
    var re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;
    // Return NULL if input string is not well formed CSV string.
    if (!re_valid.test(text)) return null;
    var a = [];                     // Initialize array to receive values.
    text.replace(re_value, // "Walk" the string using replace with callback.
        function(m0, m1, m2, m3) {
            // Remove backslash from \' in single quoted values.
            if      (m1 !== undefined) a.push(m1.replace(/\\'/g, "'"));
            // Remove backslash from \" in double quoted values.
            else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"'));
            else if (m3 !== undefined) a.push(m3);
            return ''; // Return empty string.
        });
    // Handle special case of empty last value.
    if (/,\s*$/.test(text)) a.push('');
    return a;
};

Beispiel für Ein- und Ausgabe:

In den folgenden Beispielen werden geschweifte Klammern verwendet, um die zu begrenzen {result strings}. (Dies dient zur Visualisierung von führenden / nachfolgenden Leerzeichen und Zeichenfolgen mit der Länge Null.)

// Test 1: Test string from original question.
var test = "'string, duppi, du', 23, lala";
var a = CSVtoArray(test);
/* Array hes 3 elements:
    a[0] = {string, duppi, du}
    a[1] = {23}
    a[2] = {lala} */
// Test 2: Empty CSV string.
var test = "";
var a = CSVtoArray(test);
/* Array hes 0 elements: */
// Test 3: CSV string with two empty values.
var test = ",";
var a = CSVtoArray(test);
/* Array hes 2 elements:
    a[0] = {}
    a[1] = {} */
// Test 4: Double quoted CSV string having single quoted values.
var test = "'one','two with escaped \' single quote', 'three, with, commas'";
var a = CSVtoArray(test);
/* Array hes 3 elements:
    a[0] = {one}
    a[1] = {two with escaped ' single quote}
    a[2] = {three, with, commas} */
// Test 5: Single quoted CSV string having double quoted values.
var test = '"one","two with escaped \" double quote", "three, with, commas"';
var a = CSVtoArray(test);
/* Array hes 3 elements:
    a[0] = {one}
    a[1] = {two with escaped " double quote}
    a[2] = {three, with, commas} */
// Test 6: CSV string with whitespace in and around empty and non-empty values.
var test = "   one  ,  'two'  ,  , ' four' ,, 'six ', ' seven ' ,  ";
var a = CSVtoArray(test);
/* Array hes 8 elements:
    a[0] = {one}
    a[1] = {two}
    a[2] = {}
    a[3] = { four}
    a[4] = {}
    a[5] = {six }
    a[6] = { seven }
    a[7] = {} */

Zusätzliche Bemerkungen:

Diese Lösung erfordert, dass die CSV-Zeichenfolge "gültig" ist. Beispielsweise dürfen nicht in Anführungszeichen gesetzte Werte keine Backslashes oder Anführungszeichen enthalten, z. B. ist die folgende CSV-Zeichenfolge NICHT gültig:

var invalid1 = "one, that's me!, escaped \, comma"

Dies ist keine wirkliche Einschränkung, da jede Unterzeichenfolge entweder als einfacher oder doppelter Wert dargestellt werden kann. Beachten Sie auch, dass diese Lösung nur eine mögliche Definition für: "Kommagetrennte Werte" darstellt.

Bearbeiten: 2014-05-19: Haftungsausschluss hinzugefügt. Bearbeiten: 2014-12-01: Haftungsausschluss nach oben verschoben.

Ridgerunner
quelle
1
@Evan Plaice - Danke für die schönen Worte. Natürlich können Sie jedes Trennzeichen verwenden. Ersetzen Sie einfach jedes Komma in meiner Regex durch das Trennzeichen Ihrer Wahl (das Trennzeichen kann jedoch kein Leerzeichen sein). Prost.
Ridgerunner
2
@Evan Plaice - Sie können gerne meine regulären Ausdrücke für jeden gewünschten Zweck verwenden. Eine Anerkennungsnotiz wäre nett, aber nicht notwendig. Viel Glück mit Ihrem Plug-In. Prost!
Ridgerunner
1
Cool, hier ist das Projekt code.google.com/p/jquery-csv . Schließlich möchte ich der CSV ein Erweiterungsformat hinzufügen, das SSV (Structured Separated Values) heißt und einfach CSV mit Metadaten (dh Trennzeichen, Trennzeichen, Zeilenende usw.) ist.
Evan Plaice
1
Vielen Dank für diese großartige Implementierung - ich habe sie als Basis für ein Node.js-Modul ( csv-iterator ) verwendet.
Mirkokiefer
3
Ich begrüße das Detail und die Klarstellung Ihrer Antwort, aber es sollte irgendwo angemerkt werden, dass Ihre Definition von CSV nicht zu RFC 4180 passt, was dem Standard für CSV sehr nahe kommt und von dem ich sagen kann, dass er anekdotisch häufig verwendet wird. Insbesondere wäre dies der normale Weg, um einem doppelten Anführungszeichen in einem Zeichenfolgenfeld zu "entkommen": "field one", "field two", "a ""final"" field containing two double quote marks"Ich habe die Antwort von Trevor Dixon auf dieser Seite nicht getestet, aber es handelt sich um eine Antwort, die sich mit der RFC 4180-Definition von CSV befasst.
DG.
52

RFC 4180-Lösung

Dies löst die Zeichenfolge in der Frage nicht, da ihr Format nicht mit RFC 4180 übereinstimmt. Die akzeptable Codierung entgeht einem doppelten Anführungszeichen mit einem doppelten Anführungszeichen. Die folgende Lösung funktioniert ordnungsgemäß mit CSV-Dateien d / l aus Google-Tabellen.

UPDATE (3/2017)

Das Parsen einer einzelnen Zeile wäre falsch. Laut RFC 4180 können Felder CRLF enthalten, wodurch jeder Zeilenleser die CSV-Datei beschädigt. Hier ist eine aktualisierte Version, die CSV-Zeichenfolgen analysiert:

'use strict';

function csvToArray(text) {
    let p = '', row = [''], ret = [row], i = 0, r = 0, s = !0, l;
    for (l of text) {
        if ('"' === l) {
            if (s && l === p) row[i] += l;
            s = !s;
        } else if (',' === l && s) l = row[++i] = '';
        else if ('\n' === l && s) {
            if ('\r' === p) row[i] = row[i].slice(0, -1);
            row = ret[++r] = [l = '']; i = 0;
        } else row[i] += l;
        p = l;
    }
    return ret;
};

let test = '"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF\r\n"\r\n"2nd line one","two with escaped """" double quotes""","three, with, commas",four with no quotes,"five with CRLF\r\n"';
console.log(csvToArray(test));

ALTE ANTWORT

(Einzeilige Lösung)

function CSVtoArray(text) {
    let ret = [''], i = 0, p = '', s = true;
    for (let l in text) {
        l = text[l];
        if ('"' === l) {
            s = !s;
            if ('"' === p) {
                ret[i] += '"';
                l = '-';
            } else if ('' === p)
                l = '-';
        } else if (s && ',' === l)
            l = ret[++i] = '';
        else
            ret[i] += l;
        p = l;
    }
    return ret;
}
let test = '"one","two with escaped """" double quotes""","three, with, commas",four with no quotes,five for fun';
console.log(CSVtoArray(test));

Und zum Spaß, hier ist, wie Sie CSV aus dem Array erstellen:

function arrayToCSV(row) {
    for (let i in row) {
        row[i] = row[i].replace(/"/g, '""');
    }
    return '"' + row.join('","') + '"';
}

let row = [
  "one",
  "two with escaped \" double quote",
  "three, with, commas",
  "four with no quotes (now has)",
  "five for fun"
];
let text = arrayToCSV(row);
console.log(text);

niry
quelle
1
Dieser hat den Job für mich gemacht, nicht der andere
WtFudgE
7

PEG-Grammatik (.js) für RFC 4180-Beispiele unter http://en.wikipedia.org/wiki/Comma-separated_values :

start
  = [\n\r]* first:line rest:([\n\r]+ data:line { return data; })* [\n\r]* { rest.unshift(first); return rest; }

line
  = first:field rest:("," text:field { return text; })*
    & { return !!first || rest.length; } // ignore blank lines
    { rest.unshift(first); return rest; }

field
  = '"' text:char* '"' { return text.join(''); }
  / text:[^\n\r,]* { return text.join(''); }

char
  = '"' '"' { return '"'; }
  / [^"]

Testen Sie unter http://jsfiddle.net/knvzk/10 oder https://pegjs.org/online .

Laden Sie den generierten Parser unter https://gist.github.com/3362830 herunter .

Trevor Dixon
quelle
6

Ich hatte einen ganz bestimmten Anwendungsfall, in dem ich Zellen aus Google Sheets in meine Web-App kopieren wollte. Zellen können doppelte Anführungszeichen und neue Zeilen enthalten. Beim Kopieren und Einfügen werden die Zellen durch Tabulatoren getrennt, und Zellen mit ungeraden Daten werden in doppelte Anführungszeichen gesetzt. Ich habe diese Hauptlösung ausprobiert, den verknüpften Artikel mit Regexp, Jquery-CSV und CSVToArray. http://papaparse.com/ Ist die einzige, die sofort funktioniert hat. Das Kopieren und Einfügen erfolgt nahtlos mit Google Sheets mit den Standardoptionen für die automatische Erkennung.

bjcullinan
quelle
1
Dies sollte viel höher eingestuft werden. Versuchen Sie niemals, Ihren eigenen CSV-Parser zu rollen. Dies funktioniert nicht ordnungsgemäß - insbesondere bei Verwendung von regulären Ausdrücken. Papaparse ist großartig - benutze es!
cbley
6

Die Antwort von FakeRainBrigand hat mir gefallen, sie enthält jedoch einige Probleme: Sie kann keine Leerzeichen zwischen Anführungszeichen und Komma verarbeiten und unterstützt keine zwei aufeinander folgenden Kommas. Ich habe versucht, seine Antwort zu bearbeiten, aber meine Bearbeitung wurde von Rezensenten abgelehnt, die meinen Code anscheinend nicht verstanden haben. Hier ist meine Version des Codes von FakeRainBrigand. Es gibt auch eine Geige: http://jsfiddle.net/xTezm/46/

String.prototype.splitCSV = function() {
        var matches = this.match(/(\s*"[^"]+"\s*|\s*[^,]+|,)(?=,|$)/g);
        for (var n = 0; n < matches.length; ++n) {
            matches[n] = matches[n].trim();
            if (matches[n] == ',') matches[n] = '';
        }
        if (this[0] == ',') matches.unshift("");
        return matches;
}

var string = ',"string, duppi, du" , 23 ,,, "string, duppi, du",dup,"", , lala';
var parsed = string.splitCSV();
alert(parsed.join('|'));
HammerNL
quelle
4

Die Leute schienen dafür gegen RegEx zu sein. Warum?

(\s*'[^']+'|\s*[^,]+)(?=,|$)

Hier ist der Code. Ich habe auch eine Geige gemacht .

String.prototype.splitCSV = function(sep) {
  var regex = /(\s*'[^']+'|\s*[^,]+)(?=,|$)/g;
  return matches = this.match(regex);    
}

var string = "'string, duppi, du', 23, 'string, duppi, du', lala";
var parsed = string.splitCSV();
alert(parsed.join('|'));
Räuber
quelle
3
Hmm, Ihr regulärer Ausdruck hat einige Probleme: Er kann keine Leerzeichen zwischen einem Anführungszeichen und einem Komma verarbeiten und unterstützt keine zwei aufeinander folgenden Kommas. Ich habe Ihre Antwort mit Code aktualisiert, der beide Probleme behebt, und eine neue Geige erstellt: jsfiddle.net/xTezm/43
HammerNL
Aus irgendeinem Grund wurde meine Bearbeitung Ihres Codes abgelehnt, weil sie "von der ursprünglichen Absicht des Beitrags abweichen würde". Sehr eigenartig!? Ich habe gerade Ihren Code genommen und zwei Probleme damit behoben. Wie ändert das die Absicht des Beitrags? Wie auch immer ... Ich habe dieser Frage einfach eine neue Antwort hinzugefügt.
HammerNL
Gute Frage in Ihrer Antwort, @FakeRainBrigand. Ich für meinen Teil für Regex, und aus diesem Grund erkenne ich an, dass es das falsche Werkzeug für den Job ist.
niry
2
@niry mein Code hier ist schrecklich. Ich verspreche, dass ich in den letzten 6 Jahren besser geworden bin :-p
Brigand
4

Hinzufügen eines weiteren zur Liste, da ich all das nicht ganz "KISS" genug finde.

Dieser verwendet Regex, um entweder Kommas oder Zeilenumbrüche zu finden, während er zitierte Elemente überspringt. Hoffentlich können Noobies dies selbst durchlesen. Der splitFinderreguläre Ausdruck hat drei Funktionen (geteilt durch a |):

  1. , - findet Kommas
  2. \r?\n - findet neue Zeilen (möglicherweise mit Wagenrücklauf, wenn der Exporteur nett war)
  3. "(\\"|[^"])*?"- überspringt alles, was in Anführungszeichen steht, da Kommas und Zeilenumbrüche dort keine Rolle spielen. Wenn \\"das zitierte Element ein Escape-Angebot enthält , wird es erfasst, bevor ein Endangebot gefunden werden kann.

const splitFinder = /,|\r?\n|"(\\"|[^"])*?"/g;

function csvTo2dArray(parseMe) {
  let currentRow = [];
  const rowsOut = [currentRow];
  let lastIndex = splitFinder.lastIndex = 0;
  
  // add text from lastIndex to before a found newline or comma
  const pushCell = (endIndex) => {
    endIndex = endIndex || parseMe.length;
    const addMe = parseMe.substring(lastIndex, endIndex);
    // remove quotes around the item
    currentRow.push(addMe.replace(/^"|"$/g, ""));
    lastIndex = splitFinder.lastIndex;
  }


  let regexResp;
  // for each regexp match (either comma, newline, or quoted item)
  while (regexResp = splitFinder.exec(parseMe)) {
    const split = regexResp[0];

    // if it's not a quote capture, add an item to the current row
    // (quote captures will be pushed by the newline or comma following)
    if (split.startsWith(`"`) === false) {
      const splitStartIndex = splitFinder.lastIndex - split.length;
      pushCell(splitStartIndex);

      // then start a new row if newline
      const isNewLine = /^\r?\n$/.test(split);
      if (isNewLine) { rowsOut.push(currentRow = []); }
    }
  }
  // make sure to add the trailing text (no commas or newlines after)
  pushCell();
  return rowsOut;
}

const rawCsv = `a,b,c\n"test\r\n","comma, test","\r\n",",",\nsecond,row,ends,with,empty\n"quote\"test"`
const rows = csvTo2dArray(rawCsv);
console.log(rows);

Seph Reed
quelle
Wenn ich meine Datei über fileReader und mein Ergebnis einlese: Id, Name, Age 1, John Smith, 65 2, Jane Doe, 30 Wie kann ich anhand der von mir angegebenen Spalten analysieren?
bluePearl
Nachdem Sie das 2d-Array erhalten haben, entfernen Sie den ersten Index (es sind Ihre Requisitennamen) und iterieren Sie dann über den Rest des Arrays, um Objekte mit jedem der Werte als Eigenschaft zu erstellen. Es wird so aussehen:[{Id: 1, Name: "John Smith", Age: 65}, {Id: 2, Name: "Jane Doe", Age: 30}]
Seph Reed
3

Wenn Sie Ihr Anführungszeichen durch doppelte Anführungszeichen setzen können, handelt es sich um ein Duplikat des JavaScript-Codes zum Parsen von CSV-Daten .

Sie können entweder alle einfachen Anführungszeichen zuerst in doppelte Anführungszeichen übersetzen:

string = string.replace( /'/g, '"' );

... oder Sie können den regulären Ausdruck in dieser Frage bearbeiten, um einfache Anführungszeichen anstelle von doppelten Anführungszeichen zu erkennen:

// Quoted fields.
"(?:'([^']*(?:''[^']*)*)'|" +

Dies setzt jedoch ein bestimmtes Markup voraus, das aus Ihrer Frage nicht klar hervorgeht. Bitte klären Sie anhand meines Kommentars zu Ihrer Frage, welche Möglichkeiten Markup bietet.

Phrogz
quelle
2

Meine Antwort geht davon aus, dass Ihre Eingabe Code / Inhalt aus Webquellen widerspiegelt, bei denen einfache und doppelte Anführungszeichen vollständig austauschbar sind, sofern sie als nicht maskierte Übereinstimmungsmenge auftreten.

Sie können hierfür keinen regulären Ausdruck verwenden. Sie müssen tatsächlich einen Mikroparser schreiben, um die Zeichenfolge zu analysieren, die Sie teilen möchten. Um dieser Antwort willen werde ich die zitierten Teile Ihrer Zeichenfolgen als Unterzeichenfolgen bezeichnen. Sie müssen speziell über die Schnur gehen. Betrachten Sie den folgenden Fall:

var a = "some sample string with \"double quotes\" and 'single quotes' and some craziness like this: \\\" or \\'",
    b = "sample of code from JavaScript with a regex containing a comma /\,/ that should probably be ignored.";

In diesem Fall haben Sie absolut keine Ahnung, wo eine Teilzeichenfolge beginnt oder endet, indem Sie einfach die Eingabe für ein Zeichenmuster analysieren. Stattdessen müssen Sie eine Logik schreiben, um zu entscheiden, ob ein Anführungszeichen als Anführungszeichen verwendet wird, selbst nicht in Anführungszeichen steht und dass das Anführungszeichen keinem Escape folgt.

Ich werde diese Komplexität des Codes nicht für Sie schreiben, aber Sie können sich etwas ansehen, das ich kürzlich geschrieben habe und das das Muster hat, das Sie benötigen. Dieser Code hat nichts mit Kommas zu tun, ist aber ansonsten ein ausreichend gültiger Mikroparser, damit Sie Ihren eigenen Code schreiben können. Sehen Sie sich die Asifix-Funktion der folgenden Anwendung an:

https://github.com/austincheney/Pretty-Diff/blob/master/fulljsmin.js

austincheney
quelle
2

Beim Lesen von csv zu string enthält es einen Nullwert zwischen den Strings. Versuchen Sie es also. \ 0 Zeile für Zeile funktioniert es für mich.

stringLine = stringLine.replace( /\0/g, "" );
Sharathi RB
quelle
2

Um diese Antwort zu ergänzen

Wenn Sie Anführungszeichen analysieren müssen, die mit einem anderen Anführungszeichen versehen sind, Beispiel:

"some ""value"" that is on xlsx file",123

Sie können verwenden

function parse(text) {
  const csvExp = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|"([^""]*(?:"[\S\s][^""]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g;

  const values = [];

  text.replace(csvExp, (m0, m1, m2, m3, m4) => {
    if (m1 !== undefined) {
      values.push(m1.replace(/\\'/g, "'"));
    }
    else if (m2 !== undefined) {
      values.push(m2.replace(/\\"/g, '"'));
    }
    else if (m3 !== undefined) {
      values.push(m3.replace(/""/g, '"'));
    }
    else if (m4 !== undefined) {
      values.push(m4);
    }
    return '';
  });

  if (/,\s*$/.test(text)) {
    values.push('');
  }

  return values;
}
BrunoLM
quelle
Ich fand, dass dies immer noch nicht analysiert werden kann"jjj "" kkk""","123"
am
2

Ich habe auch die gleiche Art von Problem gehabt, wenn ich eine CSV-Datei analysieren muss. Die Datei enthält eine Spaltenadresse, die das ',' enthält.
Nachdem ich diese CSV in JSON analysiert habe, erhalte ich eine nicht übereinstimmende Zuordnung der Schlüssel, während ich sie in eine JSON-Datei konvertiere.
Ich habe Node zum Parsen der Datei und der Bibliothek verwendet, wie Baby Parse und csvtojson.
Beispiel für Datei -

address,pincode
foo,baar , 123456

Während ich direkt analysierte, ohne Baby Parse in JSON zu verwenden, bekam ich

[{
 address: 'foo',
 pincode: 'baar',
 'field3': '123456'
}]

Also habe ich einen Code geschrieben, der das Komma (,) mit jedem anderen Trennzeichen in jedem Feld entfernt

/*
 csvString(input) = "address, pincode\\nfoo, bar, 123456\\n"
 output = "address, pincode\\nfoo {YOUR DELIMITER} bar, 123455\\n"
*/
const removeComma = function(csvString){
    let delimiter = '|'
    let Baby = require('babyparse')
    let arrRow = Baby.parse(csvString).data;
    /*
      arrRow = [ 
      [ 'address', 'pincode' ],
      [ 'foo, bar', '123456']
      ]
    */
    return arrRow.map((singleRow, index) => {
        //the data will include 
        /* 
        singleRow = [ 'address', 'pincode' ]
        */
        return singleRow.map(singleField => {
            //for removing the comma in the feild
            return singleField.split(',').join(delimiter)
        })
    }).reduce((acc, value, key) => {
        acc = acc +(Array.isArray(value) ?
         value.reduce((acc1, val)=> {
            acc1 = acc1+ val + ','
            return acc1
        }, '') : '') + '\n';
        return acc;
    },'')
}

Die zurückgegebene Funktion kann an die csvtojson-Bibliothek übergeben werden, sodass das Ergebnis verwendet werden kann.

const csv = require('csvtojson')

let csvString = "address, pincode\\nfoo, bar, 123456\\n"
let jsonArray = []
modifiedCsvString = removeComma(csvString)
csv()
  .fromString(modifiedCsvString)
  .on('json', json => jsonArray.push(json))
  .on('end', () => {
    /* do any thing with the json Array */
  })
Jetzt können Sie die Ausgabe wie erhalten

[{
  address: 'foo, bar',
  pincode: 123456
}]
Supermacy
quelle
2

Kein regulärer Ausdruck, lesbar gemäß https://en.wikipedia.org/wiki/Comma-separated_values#Basic_rules

function csv2arr(str: string) {
    let line = ["",];
    const ret = [line,];
    let quote = false;

    for (let i = 0; i < str.length; i++) {
        const cur = str[i];
        const next = str[i + 1];

        if (!quote) {
            const cellIsEmpty = line[line.length - 1].length === 0;
            if (cur === '"' && cellIsEmpty) quote = true;
            else if (cur === ",") line.push("");
            else if (cur === "\r" && next === "\n") { line = ["",]; ret.push(line); i++; }
            else if (cur === "\n" || cur === "\r") { line = ["",]; ret.push(line); }
            else line[line.length - 1] += cur;
        } else {
            if (cur === '"' && next === '"') { line[line.length - 1] += cur; i++; }
            else if (cur === '"') quote = false;
            else line[line.length - 1] += cur;
        }
    }
    return ret;
}
Bachor
quelle
1

Laut diesem Blog-Beitrag sollte diese Funktion Folgendes tun:

String.prototype.splitCSV = function(sep) {
  for (var foo = this.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
    if (foo[x].replace(/'\s+$/, "'").charAt(foo[x].length - 1) == "'") {
      if ((tl = foo[x].replace(/^\s+'/, "'")).length > 1 && tl.charAt(0) == "'") {
        foo[x] = foo[x].replace(/^\s*'|'\s*$/g, '').replace(/''/g, "'");
      } else if (x) {
        foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
      } else foo = foo.shift().split(sep).concat(foo);
    } else foo[x].replace(/''/g, "'");
  } return foo;
};

Sie würden es so nennen:

var string = "'string, duppi, du', 23, lala";
var parsed = string.splitCSV();
alert(parsed.join("|"));

Diese Art von jsfiddle funktioniert, aber es sieht so aus, als ob einige der Elemente Leerzeichen vor sich haben.

CanSpice
quelle
Stellen Sie sich vor, Sie müssten das alles in einem regulären Ausdruck tun. Aus diesem Grund sind reguläre Ausdrücke manchmal nicht wirklich zum Parsen geeignet.
CanSpice
Diese Lösung funktioniert einfach nicht. Angesichts der ursprünglichen "'string, duppi, du', 23, lala"["'string"," duppi"," du'"," 23"," lala"]
Testzeichenfolge
@ridgerunner: Richtig. Ich habe die Antwort und die jsfiddle bearbeitet, um die Funktion zu korrigieren. Grundsätzlich habe ich "'"zu '"'und umgekehrt gewechselt .
CanSpice
Das hat geholfen, aber jetzt verarbeitet die Funktion CSV-Zeichenfolgen mit einfachen Anführungszeichen und doppelten Anführungszeichen falsch. zB Umkehren der Anführungszeichen der ursprünglichen '"string, duppi, du", 23, lala'['"string',' duppi'.' du"',' 23',' lala']
Testzeichenfolge
@CanSpice, Ihr Kommentar hat mich dazu inspiriert, es mit RegEx zu versuchen. Es hat nicht ganz so viele Funktionen, aber sie könnten leicht hinzugefügt werden. (Meine Antwort ist auf dieser Seite, wenn Sie interessiert sind.)
Brigand
0

Abgesehen von der hervorragenden und vollständigen Antwort von ridgerunner dachte ich an eine sehr einfache Problemumgehung, wenn Ihr Backend PHP ausführt.

Fügen Sie diese PHP - Datei auf Ihrer Domain - Backend (sagen: csv.php)

<?php
session_start(); //optional
header("content-type: text/xml");
header("charset=UTF-8");
//set the delimiter and the End of Line character of your csv content:
echo json_encode(array_map('str_getcsv',str_getcsv($_POST["csv"],"\n")));
?>

Fügen Sie diese Funktion nun Ihrem Javascript-Toolkit hinzu (sollte meiner Meinung nach etwas überarbeitet werden, um Crossbrowser zu erstellen.)

function csvToArray(csv) {
    var oXhr = new XMLHttpRequest;
    oXhr.addEventListener("readystatechange",
            function () {
                if (this.readyState == 4 && this.status == 200) {
                    console.log(this.responseText);
                    console.log(JSON.parse(this.responseText));
                }
            }
    );
    oXhr.open("POST","path/to/csv.php",true);
    oXhr.setRequestHeader("Content-type","application/x-www-form-urlencoded; charset=utf-8");
    oXhr.send("csv=" + encodeURIComponent(csv));
}

Kostet Sie 1 Ajax-Anruf, aber zumindest duplizieren Sie weder Code noch fügen Sie eine externe Bibliothek hinzu.

Ref: http://php.net/manual/en/function.str-getcsv.php

Sebas
quelle
0

Sie können papaparse.js wie im folgenden Beispiel verwenden:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>CSV</title>
</head>
<body>

    <input type="file" id="files" multiple="">
    <button onclick="csvGetter()">CSV Getter</button>
    <h3>The Result will be in the Console.</h3>


<script src="papaparse.min.js"></script>
<script>
     function csvGetter() {

        var file = document.getElementById('files').files[0];
        Papa.parse(file, {
            complete: function(results) {
                console.log(results.data);
                }
           });
        }

  </script>

Vergessen Sie nicht, papaparse.js in denselben Ordner aufzunehmen.

Tahseen Alaa
quelle
0

Regelmäßige Ausdrücke zur Rettung! Diese wenigen Codezeilen verarbeiten korrekt in Anführungszeichen gesetzte Felder mit eingebetteten Kommas, Anführungszeichen und Zeilenumbrüchen basierend auf dem RFC 4180-Standard.

function parseCsv(data, fieldSep, newLine) {
    fieldSep = fieldSep || ',';
    newLine = newLine || '\n';
    var nSep = '\x1D';
    var qSep = '\x1E';
    var cSep = '\x1F';
    var nSepRe = new RegExp(nSep, 'g');
    var qSepRe = new RegExp(qSep, 'g');
    var cSepRe = new RegExp(cSep, 'g');
    var fieldRe = new RegExp('(?<=(^|[' + fieldSep + '\\n]))"(|[\\s\\S]+?(?<![^"]"))"(?=($|[' + fieldSep + '\\n]))', 'g');
    var grid = [];
    data.replace(/\r/g, '').replace(/\n+$/, '').replace(fieldRe, function(match, p1, p2) {
        return p2.replace(/\n/g, nSep).replace(/""/g, qSep).replace(/,/g, cSep);
    }).split(/\n/).forEach(function(line) {
        var row = line.split(fieldSep).map(function(cell) {
            return cell.replace(nSepRe, newLine).replace(qSepRe, '"').replace(cSepRe, ',');
        });
        grid.push(row);
    });
    return grid;
}

const csv = 'A1,B1,C1\n"A ""2""","B, 2","C\n2"';
const separator = ',';      // field separator, default: ','
const newline = ' <br /> '; // newline representation in case a field contains newlines, default: '\n' 
var grid = parseCsv(csv, separator, newline);
// expected: [ [ 'A1', 'B1', 'C1' ], [ 'A "2"', 'B, 2', 'C <br /> 2' ] ]

Sofern nicht anders angegeben, benötigen Sie keine endliche Zustandsmaschine. Der reguläre Ausdruck behandelt RFC 4180 dank positivem Lookbehind, negativem Lookbehind und positivem Lookahead ordnungsgemäß.

Klonen / Herunterladen von Code unter https://github.com/peterthoeny/parse-csv-js

Peter Thoeny
quelle