web-dev-qa-db-ja.com

CSVを分割する正規表現

私はこれ(または同様の)が何度も尋ねられたことを知っていますが、多くの可能性を試しましたが、100%動作する正規表現を見つけることができませんでした。

CSVファイルを取得し、それを配列に分割しようとしていますが、引用符付きのコンマと空の要素という2つの問題が発生しています。

CSVは次のようになります。

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

私が使用しようとした正規表現は次のとおりです。

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

唯一の問題は、出力配列で、5番目の要素が123987564として出力され、空の文字列ではないことです。

36
Code Ninja

説明

分割を使用する代わりに、単に一致を実行し、見つかった一致をすべて処理する方が簡単だと思います。

この式は:

  • サンプルテキストをカンマ区切りで区切ります
  • 空の値を処理します
  • 二重引用符がネストされていない場合、二重引用符で囲まれたコンマを無視します
  • 戻り値から区切りコンマを削除します
  • 返された値から周囲の引用符を削除します

正規表現:(?:^|,)(?=[^"]|(")?)"?((?(1)[^"]*|[^,"]*))"?(?=,|$)

enter image description here

サンプルテキスト

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

非Java式を使用したASPの例

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

非Java式を使用した一致

グループ0は、コンマを含む部分文字列全体を取得します
グループ1は、使用されている場合に見積もりを取得します
グループ2はコンマを含まない値を取得します

[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
45
Ro Yo Mi

これに少し取り組んで、このソリューションを思い付きました:

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

ここで試してみてください!

このソリューションは、「いい」CSVデータを次のように処理します。

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

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

といもののような

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

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

仕組みの説明 を次に示します。

(?:,|\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  
)
13
awwsmm

数か月前にプロジェクト用に作成しました。

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

Regular expression visualization

C#で動作し、DebuggexはPythonおよびPCREを選択したときに満足しました。Javascriptはこの形式のProceeded By?<= ...を認識しません...

あなたの値については、上のマッチを作成します

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

引用符で囲まれたものには先頭にコンマがないことに注意してください。ただし、空の値のユースケースでは、先頭のコンマと一致させる必要があります。完了したら、必要に応じて値をトリミングします。

RegexHero.Net を使用して、正規表現をテストします。

9
scott.smart

私もこの答えが必要でしたが、答えは有益でありながら、他の言語に合わせて再現するのが少し難しいと思いました。以下は、CSV行の1つの列について考えた最も単純な式です。私は分裂していません。 CSVの列に一致する正規表現を作成しているので、行を分割しません。

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

これは、CSV行の単一の列と一致します。式の最初の"([^"]*)"部分は引用符で囲まれたエントリと一致し、2番目の部分_[^,]*_は引用符で囲まれていないエントリと一致します。次に、_,_または行の終わり_$_が続きます。

そして、式をテストするための付随するdebuggex。

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

7
chubbsondubs

私はパーティーに遅れていますが、私が使用している正規表現は次のとおりです。

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

このパターンには3つのキャプチャグループがあります。

  1. 引用されたセルの内容
  2. 引用符で囲まれていないセルの内容
  3. 新しい行

このパターンは、次のすべてを処理します。

  • 特別な機能のない通常のセルの内容:one、2、three
  • 二重引用符を含むセル( "は" "にエスケープされます):no quote、" a "" quoted "" thing "、end
  • セルに改行文字が含まれています:one、two\nthree、four
  • 内部引用がある通常のセルの内容:one、two "three、four
  • セルには引用符とそれに続くコンマが含まれます:one、 "two" "three" "、four"、five

使用中のこのパターンを参照してください。

名前付きグループとルックビハインドを備えた、より有能な正規表現を使用している場合、私は次のことを好みます:

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

使用中のこのパターンを参照してください。

編集

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

このわずかに変更されたパターンは、Javascriptを使用していない限り、最初の列が空の行を処理します。何らかの理由で、Javascriptはこのパターンの2番目の列を省略します。このエッジケースを正しく処理できませんでした。

7
bublebboy

個人的には、すべてのケースに一致する完璧な表現を見つけることなく、多くの正規表現を試しました。

すべてのケースに適切に一致するように正規表現を適切に構成するのは難しいと思います。名前空間を気に入らない人はほとんどいません(私もその一部でした)。

Microsoft.VisualBasic.FileIO.TextFieldParser

ここで見つけました: StackOverflow

使用例:

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)
{
    ...

それが役立つことを願っています。

3
Eric Ouellet

従来のASPページにJScriptを使用する利点は、JavaScript用に作成された多数のライブラリのいずれかを使用できることです。

このように: https://github.com/gkindel/CSV-JS 。ダウンロードして、ASPページに含め、CSVを解析します。

<%@ 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 this pattern ",(?=([^\"]*\"[^\"]*\")*(?![^\"]*\"))"almost work for me:

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

出力:

","
",a,,"

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

欠点:列の引用符の数が奇数の場合、機能しません:(

3
marioosh

Aaaと別の答えはこちら。 :)他のquiteを動作させることができなかったので。

私の解決策は両方ともエスケープされた引用符(二重発生)を処理し、マッチに区切り文字を含めません。

注意してください' の代わりに "それは私のシナリオでしたが、同じ効果を得るためにパターンで単純に置き換えてください。

ここに行きます(「ホワイトスペースを無視」フラグを使用することを忘れないでください/x以下のコメント付きバージョンを使用する場合):

# 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
(?=,|$)

単一行バージョン:

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

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

Debuggexデモ

regex101の例

2
Johny Skovdal

私はこれを使用しています。これは、コマ区切りと二重引用符エスケープで動作します。通常、それはあなたの問題を解決するはずです:

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

エスケープされた引用符とCR/LF文字(複数行にわたる単一の値)を含む引用符で囲まれた値のサポートなど、いくつかの追加機能を備えたさらに別の回答。

注:以下のソリューションは他の正規表現エンジンに適応する可能性がありますが、それを使用してas-is正規表現エンジンが 同じ名前を使用する複数の名前付きキャプチャグループ を1つの単一キャプチャグループとして扱う必要があります。 (.NETはデフォルトでこれを行います)


CSVファイル/ストリームの複数の行/レコード(一致する RFC標準418 )が下の正規表現に渡されると、空でない各行/レコードの一致が返されます。各一致には、その行/レコードにキャプチャされた値を含むValueという名前のキャプチャグループが含まれます(オープンクオートがあった場合は、潜在的にOpenValueキャプチャグループ行/レコードの最後)

コメントされたパターンは次のとおりです(テストして 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


これは、コメントや空白を除いた生のパターンです。

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


Debuggex.comからの視覚化です (わかりやすくするために名前を付けたキャプチャグループ): Debuggex.com visualization

正規表現パターンの使用方法の例は、同様の質問への回答 here 、または C#pad here 、または here にあります。

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

この正規表現は、一重引用符と二重引用符、および別の引用符の内側で使用できます!

0
zerocool

空のフィールド(,)がないことがわかっている場合、この式はうまく機能します。

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

次の例のように...

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

ただし、空のフィールドが予想され、テキストが比較的小さい場合、解析する前に空のフィールドをスペースで置き換えて、それらが確実にキャプチャされるようにすることを検討できます。例えば...

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

また、フィールドの整合性を維持する必要がある場合は、カンマを復元して、ループ内の空のスペースをテストできます。これは最も効率的な方法ではないかもしれませんが、仕事は完了します。

0
k rey

@ -chubbsondubsが http://regex101.com で 'g'フラグを使用して投稿した正規表現を試してみると、 '、'または空の文字列のみを含む一致があります。この正規表現では:
(?:"([^"]*)"|([^,]*))(?:[,])
iはCSVの部分と一致できます(引用部分を含む)。 (行は「、」で終了する必要があります。そうでない場合、最後の部分は認識されません。)
https://regex101.com/r/dF9kQ8/4
CSVが次のような場合:
"",huhu,"hel lo",world,
4つの一致があります。

'huhu'
'こんにちは'
'世界'

0
deemon

これは私がC#で必要とするすべてに一致します:

(?<=(^|,)(?<quote>"?))([^"]|(""))*?(?=\<quote>(?=,|$))
  • 引用符を取り除きます
  • 新しい行を許可する
  • 引用符で囲まれた文字列に二重引用符を付けます
  • 引用符で囲まれた文字列にカンマを入れます
0
Ilya Rudenko

同様に、SQL挿入ステートメントからCSV値を分割する必要がありました。

私の場合、文字列は単一引用符で囲まれ、数値は囲まれていないと想定できます。

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

おそらく明白な理由のために、この正規表現は空白の結果を生成します。データ内の空の値は...,'',...ではなく...,,...として表されていたため、それらを無視できました。

0
peter