1. ホーム
  2. javascript

[解決済み] JavaScriptでカンマを含むCSVをパースするには?

2022-09-03 20:28:25

質問

以下のような文字列があります。

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

文字列をカンマごとに配列に分割したいのですが、シングルクォーテーションの外側のカンマだけです。

分割のための正しい正規表現がわかりません...。

string.split(/,/)

は私に

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

となるはずですが、結果は

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

クロスブラウザの解決策はありますか?

どのように解決するのですか?

免責事項

2014-12-01更新:以下の回答は、ある非常に特殊なCSV形式に対してのみ機能します。コメントで DG が正しく指摘したように、この解決策は RFC 4180 の CSV の定義に適合せず、MS Excel 形式にも適合しません。この解決策は、単に、文字列がエスケープされた引用符やカンマを含む可能性がある、さまざまな文字列型を含む 1 つの (非標準の) CSV 入力行を解析する方法を示しています。

非標準の CSV ソリューション

austincheney が正しく指摘しているように、エスケープ文字を含む可能性のある引用符付きの文字列を適切に処理したい場合は、本当に最初から最後まで文字列を解析する必要があります。また、OP では、quot;CSV 文字列が実際に何であるかが明確に定義されていません。まず、有効な CSV 文字列とその個々の値を構成するものを定義する必要があります。

与えられた: "CSV String" 定義

CSV文字列は、0個以上の値から構成され、複数の値はカンマで区切られます。各値の構成は次のとおりです。

  1. 二重引用符で囲まれた文字列。(エスケープされていないシングルクォートを含むことができます。)
  2. 単一引用符で囲まれた文字列。(エスケープされていない二重引用符を含む場合があります。)
  3. 非引用符で囲まれた文字列。(引用符、カンマ、バックスラッシュを含むことはできません。)
  4. 空の値。(空白を含む値は空とみなされます)。

ルール/注意事項

  • 引用された値にはカンマが含まれることがあります。
  • 引用符で囲まれた値には、エスケープされたもの、例えば 'that\'s cool' .
  • 引用符、カンマ、バックスラッシュを含む値は引用符で囲む必要があります。
  • 先頭または末尾の空白を含む値は、引用符で囲む必要があります。
  • バックスラッシュはすべて削除されます。 \' を一重引用符で囲んだ値から削除されます。
  • バックスラッシュは全て削除されます。 \" を二重引用符で囲んでいます。
  • 引用符で囲まれていない文字列は、先頭と末尾の空白が切り取られます。
  • カンマ区切りには、隣接する空白があってもかまいません (これは無視されます)。

検索します。

有効なCSV文字列(上記定義通り)を文字列値の配列に変換するJavaScript関数です。

解決方法

この解決策で使用される正規表現は複雑です。また、(IMHO) はすべて のような自明でない正規表現は、多くのコメントとインデントでフリースペースモードで表示されるべきです。残念ながら、JavaScriptはfree-spacingモードを許可していません。したがって、このソリューションで実装された正規表現は、まずネイティブな正規表現構文(Pythonのhandyを使って表現されます。 r'''...''' raw-multi-line-string構文で表現されます)。

まず最初に、CVS 文字列が上記の要件を満たしているかどうかを検証する正規表現を示します。

CSV文字列を検証するための正規表現です。

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.
"""

文字列が上記の正規表現にマッチする場合、その文字列は有効なCSV文字列であり(先に述べた規則に従って)、以下の正規表現を使用してパースすることができます。次に、以下の正規表現を使用して、CSV 文字列から 1 つの値をマッチさせます。これは、マッチする値がなくなるまで(そしてすべての値がパースされるまで)繰り返し適用されます。

有効な CSV 文字列から 1 つの値をパースするための正規表現。

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.
"""

この正規表現がマッチしない特殊な値が一つあることに注意してください - 値が空の場合、最後の値です。この特別な 最後の値が空である場合。 の場合は、次のjs関数でテストされ、処理されます。

CSV文字列をパースするJavaScript関数です。

// 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;
};

入出力例です。

以下の例では、中括弧で区切られた {result strings} . (これは、先頭/末尾のスペースと長さ0の文字列を視覚化するためです)。

// 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] = {} */

補足説明

このソリューションでは、CSV 文字列が "valid"であることが必要です。たとえば、引用符で囲まれていない値にはバックスラッシュや引用符が含まれていない可能性があります。

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

どのようなサブストリングもシングルクォートまたはダブルクォートの値として表現されるため、これは実際には制限事項ではありません。また、この解決策は、「カンマ区切り値」に対する 1 つの可能な定義に過ぎないことにも注意してください。

編集: 2014-05-19: 免責事項を追加しました。 編集:2014-12-01: 免責事項をトップに移動しました。