5

私はGmailのような検索の正規表現を理解しようとしています。

name:Joe surname:(Foo Bar)

...このトピックのように。ただし、わずかな違いがあります。。のないテキストがある場合はkey:、それも分割されるため、次のようになります。

foo:(hello world) bar:(-{bad things}) some text to search

戻ります:

foo:(hello world)
bar:(-{bad things})
some text to search
4

7 に答える 7

4

正規表現ルートを使用する際に直面する問題は、スペースの問題に遭遇することです。これを行うにはおそらく非常に複雑な正規表現がありますが、単純な正規表現の場合、検索にキーワードのスペースを含めることができないことがわかります。例:

動作:site: mysite user:john
失敗:site: "my awesome site" user:john

スペースに基づいてトークン化されているため、これは失敗します。したがって、スペースサポートが要件である場合は...

Lucene .NETエンジンの組み込みパーサーを使用してトークンを提供するか、GoldParser、Irony、Antlrなどの文法とパーサーを使用することをお勧めします。

長くて複雑に聞こえるかもしれませんが、GoldParserが実行していることを正確に実行するための文法を作成したので、文法が完了したら、実際には非常に簡単です。文法の例を次に示します。

"Name"     = 'Spruce Search Grammar'
"Version"  = '1.1'
"About"    = 'The search grammar for Spruce TFS MVC frontend'

"Start Symbol" = <Query>

! -------------------------------------------------
! Character Sets
! -------------------------------------------------
{Valid} = {All Valid} - ['-'] - ['OR'] - {Whitespace} - [':'] - ["] - ['']
{Quoted} = {All Valid} - ["] - ['']

! -------------------------------------------------
! Terminals
! -------------------------------------------------
AnyChar    = {Valid}+
Or = 'OR'
Negate = ['-']
StringLiteral   = '' {Quoted}+ '' | '"' {Quoted}+ '"'

! -- Field-specific terms
Project     = 'project' ':'
...
CreatedOn   = 'created-on' ':'
ResolvedOn  = 'resolved-on' ':'
! -------------------------------------------------
! Rules
! -------------------------------------------------

! The grammar starts below
<Query> ::= <Query> <Keywords> | <Keywords>
<SingleWord> ::= AnyChar

<Keywords> ::= <SingleWord>
              | <QuotedString> 
              | <Or> 
              | <Negate> 
              | <FieldTerms>

<Or> ::= <Or> <SingleWord> 
        | Or Negate
        | Or <SingleWord>
        | Or <QuotedString>

<Negate> ::= <Negate> Negate <SingleWord>
            | <Negate> Negate <QuotedString>
            | Negate <SingleWord>
            | Negate <QuotedString>

<QuotedString> ::= StringLiteral

<FieldTerms> ::= <FieldTerms> Project | <FieldTerms> Description | <FieldTerms> State 
                | <FieldTerms> Type | <FieldTerms> Area | <FieldTerms> Iteration 
                | <FieldTerms> AssignedTo | <FieldTerms> ResolvedBy 
                | <FieldTerms> ResolvedOn | <FieldTerms> CreatedOn
                | Project 
                | <Description>
                | State 
                | Type 
                | Area 
                | Iteration 
                | CreatedBy
                | AssignedTo 
                | ResolvedBy
                | CreatedOn
                | ResolvedOn

<Description> ::= <Description> Description | <Description> Description StringLiteral
                | Description | Description StringLiteral

これにより、次のような検索サポートが提供されます。

解決済み-by:johnプロジェクト:"すばらしいtfsプロジェクト"

トークンを見るKeywordsと、単一の単語、OR、引用符で囲まれた文字列、または負の数(NOT)を期待していることがわかります。難しい部分は、この定義が再帰的になるときに発生します。これは、この<Description>部分に見られます。

構文はEBNFと呼ばれ、言語の形式を記述します。検索クエリパーサーのような単純なもの、またはコンピューター言語全体を記述できます。Goldparserがトークンを解析する方法は、トークン(LALR)を先読みするため、制限されます。そのため、HTMLやWiki構文などの言語では、タグやトークンを閉じる必要がないため、書き込もうとする文法が壊れます。 。AntlrはLL(*)を提供します。これは、開始タグ/トークンの欠落をより許容しますが、検索クエリパーサーについて心配する必要はありません。

私の文法とC#コードのコードフォルダーは、このプロジェクトにあります。

QueryParserは検索文字列を解析するクラスであり、文法ファイルは.grmファイルであり、2mbファイルは、Goldparserが文法を最適化して基本的に独自の可能性のテーブルを作成する方法です。CalithaはGoldParserのC#ライブラリであり、実装が簡単です。さらに大きな答えを書かなければ、それがどのように行われるかを正確に説明するのは難しいですが、文法をコンパイルするとかなり簡単です。Goldparserには、SQL、C#などの既存の文法を含む非常に直感的なIDEがあります。 Java、さらにはPerlの正規表現もあると思います。

正規表現から得られるような1時間の迅速な修正ではありませんが、2〜3日近くかかりますが、「適切な」構文解析の方法を学びます。

于 2012-05-25T11:20:37.650 に答える
4

単一の正規表現を使用して必要なものすべてを取得する方法はありません。問題は、キーワードのないテキストを取得する信頼できる方法がないことです。

ただし、最初にすべてのキーワードテキストを取得して保存し、次に(同じ正規表現を使用して)正規表現を空の文字列に置き換えると、突然検索文字列が自動的に取得されます。

  1. 次の正規表現を使用して、キーワードと関連テキストを取得します(RegExrで参照)。

    ([a-zA-Z] +:(?:\([^)] +?\)| [^(] +))
  2. 次に、空の文字列を使用して、完全な検索文字列に対して同じ正規表現で正規表現置換を実行します。結果の文字列は、キーワードではない検索テキストになります。次のようなもの:

    Regex.Replace(searchtext、@ "[a-zA-Z] +:(?:\([^)] +?\)| [^(] +)"、 "");
    
  3. 検索テキストの最初と最後で空白のトリミングを実行します

  4. 検索テキストからダブル(またはそれ以上のスペース)を削除します(正規表現の置換、シングルスペースでの置換で実行できます):

    Regex.Replace(searchtext、@ "{2、}"、 "");
                                ^-スペースに注意してください:)
    
  5. ????

  6. 利益!!!

#2の正規表現で空白の削除を実行することは完全に可能ですが、正規表現を処理するときは、可能な限りクリーンに保つことを好む傾向があります。

于 2012-05-25T13:54:54.607 に答える
0

ここでの簡単なアプローチは、文字列を次のパターンと一致させることです。

\w+:(?:\([^)]*\)|\S+)|\S+

それは一致します:

  • \w+:- かぎ。
  • (?:)- に続く...
    • \([^)]*\)-括弧
    • |- また
    • \S+-スペースではない一部の文字。
  • |\S+-または、1つの単語に一致します。

このパターンは、単語をさまざまな一致に分割することに注意してください。本当にそれを処理できない場合|(?:\S+(\s+(?!\w*:)[^\s:]+)*)は、最後のの代わりにのようなものを使用できます|\S+

実例: http: //ideone.com/bExFd

于 2012-05-25T21:54:13.893 に答える
0

もう1つのオプション、もう少し堅牢です。
ここでは、.Netパターンのやや高度な機能を使用できます。これらはすべてのグループのすべてのキャプチャを保持します。これは、完全なパーサーを構築するための便利な機能です。ここに、引用符で囲まれた文字列や演算子(ORまたは範囲..など)などの他の検索機能をいくつか含めました。

\A
(?>
    \s                      # skip over spaces.
    |
    (?<Key>\w+):            # Key:
    (?:                     # followed by:
        \(                     
        (?<KeyValue>[^)]*)      # Parentheses
        \)
        |                       # or
        (?<KeyValue>\S+)        # a single word
    )
    |
    (?<Operator>OR|AND|-|\+|\.\.)
    |
    ""(?<Term>[^""]*)""     # quoted term
    |
    (?<Term>\w+)            # just a word
    |
    (?<Invalid>.)           # Any other character isn't valid
)*
\z

すべてのトークンとその位置を簡単に取得できるようになりました(KeyキャプチャとKeyValueキャプチャをzip形式で圧縮してペアにすることもできます)。

Regex queryParser = new Regex(pattern, RegexOptions.IgnorePatternWhitespace);
Match m = queryParser.Match(query); // single match!
// ...
var terms = m.Groups["Term"].Captures;

実例: http: //ideone.com/B7tln

于 2012-05-25T21:57:10.087 に答える
0

この質問をご覧ください。

次の正規表現サンプルが含まれています。

^((?!hede).)*$ 

回答の作成者が述べているように、「上記の正規表現は、(サブ)文字列'hede'を含まない、任意の文字列、または改行のない行に一致します。」

したがって、これを投稿したトピックの情報および上記の正規表現と組み合わせて、問題を解決できるはずです。

お役に立てれば!!!

于 2012-05-25T11:19:24.840 に答える
0

これはあなたのために働くかもしれません

Javaの場合:

p = Pattern.compile("(\\w+:(\\(.*?\\))|.+)\\s*");
m = p.matcher("foo:(hello world) bar:(-{bad things}) some text to search");
while(m.find()){
    Log.v("REGEX", m.group(1));
}

生産:

05-25 15:21:06.242:V / REGEX(18203):foo :( hello world)
05-25 15:21:08.061:V / REGEX(18203):bar:(-{bad things})
05-25 15:21:09.761:V / REGEX(18203):検索するテキスト

タグが最初でフリーテキストが最後である限り、正規表現は機能します。
タグの場合でも、次のコンテンツを取得できますm.group(2)

于 2012-05-25T12:29:10.717 に答える
0

1つの正規表現だけを使用してこの問題を解決する必要はありません。あなたが部分的に機能することを示したあなたがリンクした答えを再利用することができます。

最後の配列要素は、修正が必要な唯一の要素です。

あなたの例を使用すると、最初に得られるでしょう:

[
    "foo:(hello world)",
    "bar:(-{bad things}) some text to search"
]

最後の項目は、最初の閉じ括弧とそれに続くテキストまでのテキストに分割する必要があります。次に、最後の項目を角かっこまでのテキストに置き換えてから、それに続くテキストを配列に追加します。

[
    "foo:(hello world)",
    "bar:(-{bad things})",
    "some text to search"
]

次の擬似コードは、これを行う方法を説明する必要があります。

array; // Array returned when string was split using /\s+(?=\w+:)/
lastPosition = array.length-1;

lastElem = array[lastPosition]; // May contain text without a key

// Key is followed by an opening bracket
//  (check for opening bracket after semi-colon following key)
if ( lastElem.match( /^[^:]*:(/ ) ) {
    // Need to replace array entry with key and all text up to and including
    // closing bracket.
    // Additional text needs to be added to array.

    maxSplitsAllowed = 1;
    results = lastElem.split( /)\w*/ , maxSplitsAllowed );
    // White space following the bracket was included in the match so it
    //  wouldn't be at the front of the text without a key

    lastKeyAndText = results[0] + ')'; // Re-append closing bracket
    endingTextWithoutKey = results[1];

    array[lastPosition] = lastKeyAndText; // Correct array entry for last key
    array.append( endingTextWithoutKey ); // Append text without key

// Key is not followed by a closing bracket but has text without a key
//  (check for white space following characters that aren't white space
//   characters)
} else if (lastElem.match( /^[^:]*:[^\w]*\w/ )) {
    // Need to change array entry so that all text before first space
    // becomes the key.
    // Additional text needs to be added to array.

    maxSplitsAllowed = 1;
    results = lastElem.split( /\w+/ , maxSplitsAllowed );

    lastKeyAndText = results[0];
    endingTextWithoutKey = results[1];

    array[lastPosition] = lastKeyAndText; // Correct array entry for last key
    array.append( endingTextWithoutKey ); // Append text without key
}

キーに続くテキスト内に空白文字を含める場合は、角かっこが必要であると想定しました。

于 2012-05-28T18:42:47.577 に答える