web-dev-qa-db-ja.com

引用符の外でカンマを選択する正規表現

これが可能かどうかはよくわかりません。

引用セットの外にあるすべてのコンマを選択する正規表現を見つけたいのですが。

例えば:

'foo' => 'bar',
'foofoo' => 'bar,bar'

これにより、1行目の'bar',の後の単一のカンマが選択されます。

一重引用符と二重引用符については特に気にしません。

誰かが何か考えを持っていますか?これは先読みで可能だと思いますが、私の正規表現fuは弱すぎます。

41
SocialCensus

これは、引用符で囲まれていない最初の「、」までの任意の文字列に一致します。それはあなたが望んでいることですか?

/^([^"]|"[^"]*")*?(,)/

あなたがそれらすべてを望んでいるなら(そしてそれは不可能だと言った人への反例として)あなたは書くことができます:

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

それらのすべてに一致します。したがって

'test, a "comma,", bob, ",sam,",here'.gsub(/(,)(?=(?:[^"]|"[^"]*")*$)/,';')

引用符内のすべてのコンマnotをセミコロンに置き換え、以下を生成します。

'test; a "comma,"; bob; ",sam,";here'

改行を越えて機能する必要がある場合は、m(複数行)フラグを追加するだけです。

88
MarkusQ

以下の正規表現は、二重引用符の外側にあるすべてのコンマに一致します。

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

[〜#〜] demo [〜#〜]

[〜#〜]または[〜#〜](PCREのみ)

"[^"]*"(*SKIP)(*F)|,

"[^"]*"は、二重引用符で囲まれたすべてのブロックに一致します。つまり、このbuz,"bar,foo"入力では、この正規表現は"bar,foo"のみに一致します。次の(*SKIP)(*F)は、一致を失敗させます。次に、|記号の隣にあるパターンに移動し、残りの文字列の文字を照合します。つまり、出力では、パターン,の横の|は、buzの直後のコンマのみに一致します。二重引用符で囲まれた部分は既にスキップするため、二重引用符で囲まれたコンマとは一致しないことに注意してください。

[〜#〜] demo [〜#〜]


以下の正規表現は、二重引用符内にあるすべてのコンマに一致します。

,(?!(?:[^"]*"[^"]*")*[^"]*$)

[〜#〜] demo [〜#〜]

15
Avinash Raj

正規表現でハックすることは可能ですが(次の人と同じように正規表現を乱用することを楽しんでいます)、高度なパーサーなしで部分文字列を処理しようとすると、問題が発生します。問題が発生する可能性のある方法には、引用符の混合、エスケープされた引用符などがあります。

この関数は、コンマで文字列を分割しますが、単一引用符または二重引用符で囲まれた文字列内のコンマは分割しません。これは、引用符として使用する追加の文字で簡単に拡張できます(ただし、""のような文字のペアには、さらに数行のコードが必要です)。データ内の引用符を閉じるのを忘れた場合でも通知されます。

function splitNotStrings(str){
  var parse=[], inString=false, escape=0, end=0

  for(var i=0, c; c=str[i]; i++){ // looping over the characters in str
    if(c==='\\'){ escape^=1; continue} // 1 when odd number of consecutive \
    if(c===','){
      if(!inString){
        parse.Push(str.slice(end, i))
        end=i+1
      }
    }
    else if(splitNotStrings.quotes.indexOf(c)>-1 && !escape){
      if(c===inString) inString=false
      else if(!inString) inString=c
    }
    escape=0
  }
  // now we finished parsing, strings should be closed
  if(inString) throw SyntaxError('expected matching '+inString)
  if(end<i) parse.Push(str.slice(end, i))
  return parse
}

splitNotStrings.quotes="'\"" // add other (symmetrical) quotes here
2
Touffy

次の正規表現を試してください:

(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*=>\s*(?:"(?:[^\\"]+|\\(?:\\\\)*[\\"])*"|'(?:[^\\']+|\\(?:\\\\)*[\\'])*')\s*,

これにより、「'foo\'bar' => 'bar\\',」。

1
Gumbo

@ SocialCensus、MarkusQへのコメントであなたが 'と一緒に'で投げた例は、変更した場合sam tosam's:(test、a "comma、"、bob、 "、sam's、"、here)は(、)(?=(?:[^ " '] | ["|'] [^" '] ")$)。実際、問題自体は、「一重引用符と二重引用符のどちらも気にしません」 、があいまいです。 "または 'で引用することにより、意味を明確にする必要があります。たとえば、ネストは許可されていますか?もしそうなら、何レベルまで?ネストされたレベルが1つだけの場合、内側のネストされた引用の外側で外側のネストされた引用の内側のコンマはどうなりますか?また、単一引用符はアポストロフィとして単独で発生することも考慮する必要があります(つまり、前にsamで示した反例のように)。最後に、作成した正規表現は、最後のタイプの引用符が必ず二重引用符であると想定しているため、実際には一重引用符を二重引用符で処理しません。そして、最後の二重引用符を['| "]で置き換えることにも問題があります。ただし、テキストに正しい引用符が付いていない場合(またはアポストロフィが使用されている場合)は、おそらくすべての引用符が正しく記述されていると想定できます。

MarkusQの正規表現は、質問に答えます。二重引用符の後に偶数の二重引用符がある(つまり、二重引用符の外側にある)すべてのコンマを見つけ、奇数の二重引用符の後にあるすべてのコンマ(つまり、二重引用符の内側にある)を無視します。これは通常、おそらく必要なものと同じソリューションですが、いくつかの異常を見てみましょう。最初に、誰かが最後に引用符を残した場合、この正規表現は、目的のコンマを見つけたり、いずれにも一致しないのではなく、すべての間違ったコンマを見つけます。もちろん、二重引用符が欠落している場合、欠落しているものが最後に属しているか、または最初に属しているかが明確でない可能性があるため、すべての賭けは無効です。ただし、正当であり、正規表現が失敗すると思われる場合があります(これが2番目の「異常」です)。テキスト行をまたぐように正規表現を調整する場合、複数の連続する段落を引用するには、各段落の先頭に単一の二重引用符を配置し、引用符を除いて各段落の末尾に引用符を省略する必要があることに注意してください。最後の段落の終わり。これは、それらの段落のスペースを超えて、正規表現がいくつかの場所で失敗し、他の場所で成功することを意味します。

段落の引用とネストされた引用の例と簡単な説明は、ここ http://en.wikipedia.org/wiki/Quotation_mark にあります。

1
Jose_X

MarkusQの回答は、約1年間、うまくいかなかったときまでうまくいきました。約120のコンマと合計3682文字の行でスタックオーバーフローエラーが発生しました。 Javaでは、このように:

        String[] cells = line.split("[\t,](?=(?:[^\"]|\"[^\"]*\")*$)", -1);

スタックオーバーフローを起こさない、非常に洗練されていない代替品を次に示します。

private String[] extractCellsFromLine(String line) {
    List<String> cellList = new ArrayList<String>();
    while (true) {
        String[] firstCellAndRest;
        if (line.startsWith("\"")) {
            firstCellAndRest = line.split("([\t,])(?=(?:[^\"]|\"[^\"]*\")*$)", 2);
        }
        else {
            firstCellAndRest = line.split("[\t,]", 2);                
        }
        cellList.add(firstCellAndRest[0]);
        if (firstCellAndRest.length == 1) {
            break;
        }
        line = firstCellAndRest[1];
    }
    return cellList.toArray(new String[cellList.size()]);
}
1
sullivan-