1

RegEx を使用しているときに奇妙な動作が発生します。

dataString = "#Name #Location New York #Rating"
string[] rawValues = Regex.Split(dataString.Trim(), "(^|\\s)+#\\w+");

パターンが一致します: "#Name", " #Location", " #Rating"(これは私が一致させようとしているものです)。
分割は次を返します。["", "", "", " ", "New York", " ", ""]

質問 #1: カンフュージョンはここから始まります。位置に空の文字列があるのはなぜ0,1,2ですか? 文字列の最初の位置にあったため、一致するものと 1 つに 2 つですか?

しかし、これは奇妙な部分ではありませんでした。

string[] rawValues = Regex.Split(dataString.Trim(), "(\\s|^)+#(\\w*[A-Za-z_]+\\w*)");

パターンは次のように一致します: "#Name", " #Location", " #Rating"(前と同じ)。
しかし、分割は次のように返します。["", "", "Name", "", " ", "Location"," New York", " ", "Rating",""]

質問 #2: まったく同じ一致につながるパターンは、まったく異なる分割出力になります。これはどのように可能ですか??

4

2 に答える 2

2

その理由は、MSDNの次の文です。

Regex.Split 式でキャプチャ用の括弧が使用されている場合、キャプチャされたテキストは結果の文字列配列に含まれます。

Split一致した文字列を本当に分割したいだけの場合は、キャプチャ グループを使用しないでください。(?:...)every の代わりに使用することで、グループのキャプチャを回避でき(...)ます。

さらに、あなたが正しく想定したように。最初と最後""は、文字列が一致で開始および終了するという事実に由来します (したがって、これらの一致の前後の空の文字列が分割で報告されます)。

目的により適した正規表現を次に示します。

@"(?:^|\s+)#\w*[A-Za-z_]+\w*"

+最初のサブパターンの外側を持つことも不要であり、扱いにくい副作用につながることに注意してください。まず、グループが複数回キャプチャできるようにしました (これが、2 つの追加"",を取得した理由です"": 1 つは 用^、もう 1 つは 用です\s)。^第 2 に、最初のスペース文字が一致した後は繰り返す必要がないため、スペース文字のみを繰り返すだけで十分です。また、単語をグループ化する必要はまったくありません#

#nameただし、文字列の先頭にある場合やスペースが先行している場合 (つまり、スペース以外の文字が先行していない場合) のように一致させたい場合は、一致に可能なスペースを含める必要はありません。否定的な後読みは、良い方法です。

@"(?<!\S)#\w*[A-Za-z_]+\w*"

これはまさに上で説明したことを行います。(?<!\S)は、スペース以外の文字が残っていない場合に一致します (スペース文字がある場合は、一致にスペース文字を含めません) 。これは、代替なしで両方のケースをカバーし、キー名を指定する必要はありません。Trim

于 2012-11-27T17:52:11.260 に答える
0

分割する正規表現は、1 つ以上の空白の後にハッシュ ('#') が続き、その後に 1 つ以上の単語文字が続くものと一致するためです。

結果に含まれないものはすべて一致します。

これを行うには、次の 2 つの方法があります。

  1. 不要なものに分割し、結果をフィルタリングします。
  2. 欲しいものだけを積極的に探す。

上記のオプションの両方を使用したコードを次に示します。

static void Main( string[] args )
{
    string   sourceText = "#Name #Location New York #Rating" ;

    // option 1: split on whitespace and then toss whatever isn't wanted
    string[] hashTokens1 = sourceText.Split().Where( x => x.StartsWith("#") ).ToArray() ;

    // option 2: actively search for what is desired
    string[] hashTokens2 = ParseSourceData( sourceText ).ToArray() ;

    return ;

}

private static readonly Regex hashTokenPattern = new Regex( @"#\w+");
private static IEnumerable<string> ParseSourceData( string s )
{
    for ( Match m = hashTokenPattern.Match( s ) ; m.Success ; m = m.NextMatch() )
    {
        yield return m.Value ;
    }
}

私自身、あなたが達成しようとしていることのケースをより適切に述べているので、2番目のオプションを使用します。良い一般的なルールは、否定的なものよりも肯定的なアサーションまたはテストを好むことです。

2番目のオプションを「ワンライナー」として書くこともできます。

// option 2: actively search for what is desired
Regex hashTokenPattern = new Regex( @"#\w+");
string[] hashTokens2 = hashTokenPattern.Matches(sourceText).Cast<Match>().Select(x=>x.Value).ToArray();
于 2012-11-27T18:03:14.463 に答える