3

newb の質問で申し訳ありませんが、C# は私の最初の言語ではありません。

句読点を考慮して、特定のコンテンツ内の単語間のすべての区切り記号のインデックス リストを作成しようとしています。Regex \b (単語「境界」) を使用したいと思っていましたが、予期していなかったあらゆる種類のものに一致しています。ここに私が書いた方法があります:

internal static IList<int> GetBreakIndexesInContent(string content)
{
    IList<int> indices = new List<int>();
    if (content != null) 
    {
        foreach (Match match in Regex.Matches(content, @"\b"))
        {
            Console.WriteLine("INDEX:[" + match.Index + "]   CHAR:[" + content.Text[match.Index] + "]   UNICODE:[" + (int)content.Text[match.Index] + "]");
            indices.Add(match.Index);
        }
    }
    return indices;
}

次の 100 文字の文字列があるとします。

"Lorem ipsum dolor sit amet, tritani quaestio suscipiantur mea ea, duo et impedit facilisi evertitur."

私のメソッドは、長さが 14 要素のリストを生成することを期待しています。最初のインデックスは位置 5、2 番目の位置は 11 というようになります (位置 26 と 64 のコンマ、および 99 のピリオドは無視します)。代わりに、これは私が得ている出力です:

//COUNT: [30]
INDEX:[0]   CHAR:[L]   UNICODE:[76]
INDEX:[5]   CHAR:[ ]   UNICODE:[32]
INDEX:[6]   CHAR:[i]   UNICODE:[105]
INDEX:[11]   CHAR:[ ]   UNICODE:[32]
INDEX:[12]   CHAR:[d]   UNICODE:[100]
INDEX:[17]   CHAR:[ ]   UNICODE:[32]
INDEX:[18]   CHAR:[s]   UNICODE:[115]
INDEX:[21]   CHAR:[ ]   UNICODE:[32]
INDEX:[22]   CHAR:[a]   UNICODE:[97]
INDEX:[26]   CHAR:[,]   UNICODE:[44]
INDEX:[28]   CHAR:[t]   UNICODE:[116]
INDEX:[35]   CHAR:[ ]   UNICODE:[32]
INDEX:[36]   CHAR:[q]   UNICODE:[113]
INDEX:[44]   CHAR:[ ]   UNICODE:[32]
INDEX:[45]   CHAR:[s]   UNICODE:[115]
INDEX:[57]   CHAR:[ ]   UNICODE:[32]
INDEX:[58]   CHAR:[m]   UNICODE:[109]
INDEX:[61]   CHAR:[ ]   UNICODE:[32]
INDEX:[62]   CHAR:[e]   UNICODE:[101]
INDEX:[64]   CHAR:[,]   UNICODE:[44]
INDEX:[66]   CHAR:[d]   UNICODE:[100]
INDEX:[69]   CHAR:[ ]   UNICODE:[32]
INDEX:[70]   CHAR:[e]   UNICODE:[101]
INDEX:[72]   CHAR:[ ]   UNICODE:[32]
INDEX:[73]   CHAR:[i]   UNICODE:[105]
INDEX:[80]   CHAR:[ ]   UNICODE:[32]
INDEX:[81]   CHAR:[f]   UNICODE:[102]
INDEX:[89]   CHAR:[ ]   UNICODE:[32]
INDEX:[90]   CHAR:[e]   UNICODE:[101]
INDEX:[99]   CHAR:[.]   UNICODE:[46]

単純 " "に ASCII 32 のフィルタリングのみを試みているわけではない理由は、すべての単語の間に必ずしも空白を使用していない外国語に対応する必要があるためです。また、意図せずに複数のスペースを個々の「セパレーター」としてキャプチャしたくないためです。

真の単語分離のための優れた標準キャッチオールになることを本当に望ん\bでいましたが、そうではないようです. 私は「自分自身を転がす」ことができましたが、C# にこの問題を処理するための何らかの機能が既にある場合は、車輪を再発明する手間を省けることを望んでいました。

もちろん、どんな助けでも大歓迎です。

ありがとう、グレッグ。

4

3 に答える 3

2

正規表現 ( \w) の単語文字の定義がニーズ (続きを読む) を満たす場合、単語以外の文字 (たとえば、逆文字クラス を使用して単語間の挿入物) と一致さ\Wせることができます。解決策は次のようになります。単純な

private static readonly Regex rxWord = new Regex( @"\w+" ) ;
static IEnumerable<string> ParseWords( string s )
{
  return rxWord.Matches(s).Cast<Match>().Select( m => m.Value ) ;
}

private static Regex rxNonWord = new Regex( @"\W+" ) ;
private static IEnumerable<string> ParseNonWords( string s )
{
  return rxNonWord.Matches(s).Cast<Match>().Select( m => m.Value ) ;
}

しかし、あなたがやろうとしていることから、CLR がサポートする Unicode カテゴリから文字クラスまたは単語区切り記号を作成する方が簡単かもしれません。

さらに、正規表現の「単語」クラスと「非単語」クラス (\wおよび\W) を使用し、それらの間の境界 ( \b) を使用することはおそらく機能しません。文字クラス\wは、C 言語の識別子 ( ) で使用できる文字のセットとして誕生しました[A-Za-z0-9_]。正規表現を使用してシンボルのソース コードを grep する C プログラマーにとって非常に便利です。単語の任意のテキストをかき回すのにはあまり適していません。

in CLR 正規表現の現在の定義では\w、次の Unicode カテゴリのいずれかに含まれる任意の文字に一致します。

  • Li (文字、小文字)
  • Lu (文字、大文字)
  • Lt (レター、タイトルケース)
  • ロー(手紙、その他)
  • Lm (文字、修飾子)
  • Nd (数値、10 進数)
  • Pc (句読点、コネクタ) このカテゴリには 10 文字が含まれます。ここで、少なくとも英語で最もよく見られるのは_(0x005F) 別名アンダースコアまたは LOWLINE です。

言うまでもなく、それ\wは怠惰な書き方[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Lm}\p{Nd}\p{Pc}]です。

非単語文字クラス\Wはこれの逆です。と言うのとまったく同じです[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Lm}\p{Nd}\p{Pc}]

ゼロ幅のアンカー\bは何にも「一致」しません: 姉妹の^とのように、一致を特定の場所$に固定します。の場合、その場所は単語 ( ) と単語以外 ( )の文字の境界です。には、逆に一致するいとこがあります。これは、2 つの単語 ( ) または 2 つの非単語 ( ) 文字の間の境界で一致を固定します。\b \b\w\W\b\B\w\W

そう...

まず、問題のドメインに適合する「単語」の定義を考え出す必要があります。これは思ったより難しいです: たとえば、「23」は 1 語ですか、それとも 2 語ですか? 「元妻」はどうですか?または、「抽象表現主義」のような複合語はどうでしょうか。文脈に応じて 1 つまたは 2 つの単語になります (辞書の個々のエントリとして「抽象」、「表現主義」、「抽象表現主義」を見つけることができます)。

その定義を満たす文字クラスを定義できれば、すべてうまくいきます。単語間のインタースティシャルを一致させるには、逆文字クラスを定義するだけです。

単純な文字クラスでうまくいかない場合は、さまざまな先読み/後読みアサーションを使用して、必要なものに一致させる必要があります。

于 2013-10-29T21:55:21.880 に答える
1

こんなに長いコメントを書くつもりはありませんでした。私はそれを答えに移したほうがいいと思います。

\b単語と単語以外の文字の間、つまり\wとの間\W、文字列の先頭と最初の文字の間、文字とスペース (スペースの両側) などの間のすべての境界に一致します。

目的を達成するために、式をルックアラウンド アサーションと組み合わせる必要がある場合があります。

例えば、

\b(?<=[a-zA-Z])

文字に続く単語の境界のみを一致させるために、肯定的な後読みアサーションを使用しますただし、これはスペース区切り文字を考慮します。これは、あなたがやりたいかどうかわかりません。その場合、

\b(?<=[a-zA-Z])(?!\s)

追加の条件を追加します。今回は、空白文字が続かない単語境界のみに一致することを保証するための否定先読みアサーションです。

于 2013-10-29T20:38:21.420 に答える
1

単語境界一致位置の例:

 In   Lorem   ipsum   dolor   sit   amet, 
^  ^ ^     ^ ^     ^ ^     ^ ^   ^ ^    ^^  

そのため、思ったよりも多くの一致を見ることができます。

技術的には、境界はアサーションです。アサーションは文字の「間」に存在します。
登場人物の間に座ると、前か後ろを見る傾向があります。

したがって、次のいずれか\bである可能性があります(?<=\w)(?=\W|$)(?<=\W|^)(?=\w)

于 2013-10-29T20:34:03.287 に答える