web-dev-qa-db-ja.com

Javascript RegEx + Word境界+ Unicode文字

私は検索を構築しており、JavaScriptオートコンプリートを使用します。私はフィンランド(フィンランド語)出身なので、ä、ö、åなどの特殊文字を処理する必要があります。

ユーザーが検索入力フィールドにテキストを入力すると、テキストをデータに一致させようとします。

次に、ユーザーが「ää」などと入力した場合に正しく機能しない簡単な例を示します。 「äl」と同じ

var title = "this is simple string with finnish Word tämä on ääkköstesti älkää ihmetelkö";
// Does not work
var searchterm = "äl";

// does not work
//var searchterm = "ää";

// Works
//var searchterm = "wi";

if ( new RegExp("\\b"+searchterm, "gi").test(title) ) {
    $("#result").html("Match: ("+searchterm+"): "+title);
} else {
    $("#result").html("nothing found with term: "+searchterm);   
}

http://jsfiddle.net/7TsxB/

では、これらのä、öとåの文字をjavascript regexで動作させるにはどうすればよいですか?

私はユニコードコードを使用するべきだと思いますが、どうすればいいですか?それらの文字のコードは次のとおりです:[\ u00C4、\ u00E4、\ u00C5、\ u00E5、\ u00D6、\ u00F6]

=>äÄåÅöÖ

43
user1394520

正規表現とWordの境界\bに、文字列の先頭と通常の256バイトの範囲外の開始文字とのマッチングに問題があるようです。

\bを使用する代わりに、(?:^|\\s)を使用してみてください

var title = "this is simple string with finnish Word tämä on ääkköstesti älkää ihmetelkö";
// Does not work
var searchterm = "äl";

// does not work
//var searchterm = "ää";

// Works
//var searchterm = "wi";

if ( new RegExp("(?:^|\\s)"+searchterm, "gi").test(title) ) {
    $("#result").html("Match: ("+searchterm+"): "+title);
} else {
    $("#result").html("nothing found with term: "+searchterm);   
}

壊す:

(?:括弧()は、正規表現のキャプチャグループを形成します。括弧は疑問符で始まり、コロン?:は非キャプチャグループを形成します。用語をグループ化するだけです

^キャレット記号は文字列の先頭と一致します

|バーは「または」演算子です。

\sは空白に一致します(バックスラッシュをエスケープする必要があるため、文字列では\\sとして表示されます)

)はグループを閉じます

したがって、Wordの境界に一致し、Unicode文字には機能しない\bを使用する代わりに、文字列の先頭に一致する非キャプチャグループを使用しますOR空白文字。

39
mowwwalker

JavaScript RegExの\b文字クラスは、単純なASCIIエンコーディングでのみ実際に役立ちます。\bは、\w\Wのセットまたは\wとストリングの先頭または末尾の境界のショートカットコードです。これらは文字セットは、ASCII "Word"文字のみを考慮します。ここで、\w[a-zA-Z0-9_]に等しく、\Wはそのクラスの否定です。

このため、RegEx文字クラスは、実際の言語を処理するのにほとんど役に立ちません。

\sは、検索用語が空白文字で区切られている場合に限り、目的どおりに機能するはずです。

18
Noah Freitas

この質問は古いですが、ユニコード文字を使用した正規表現の境界のより良い解決策を見つけたと思います。 XRegExpライブラリを使用すると、これを拡張する有効な\ b境界を実装できます

XRegExp('(?=^|$|[^\\p{L}])')

結果は4000文字以上になりますが、非常にうまく機能しているようです。

いくつかの説明:(?=)は、開始または終了の境界または非文字のUnicode文字を検索する長さゼロの先読みです。\bは何もキャプチャしないので、最も重要な考えは先読みです。それは単にtrueまたはfalseです。

13
max masetti

Unicodeの特定の文字セットを使用する必要がある場合は XRegExp を使用することをお勧めします。このライブラリの作成者は、さまざまな地域の文字セットをマッピングして、さまざまな言語での作業を容易にしました。

6
micnic

\bは、文字と文字以外の文字の間、またはその逆への移行のショートカットです。

max_masseti の回答の更新と改善:

ES2018のRegExの/u修飾子の導入により、\p{L}を使用して任意のUnicode文字を表し、\P{L}(大文字のP)を使用して表すことができるようになりました何でも。

[〜#〜] edit [〜#〜]:以前のバージョンは不完全でした。

など:

const text = 'A Fé, o Império, e as terras viciosas';

text.split(/(?<=\p{L})(?=\P{L})|(?<=\P{L})(?=\p{L})/);

// ['A', ' Fé', ',', ' o', ' Império', ',', ' e', ' as', ' terras', ' viciosas']

後読み(?<=...)を使用して文字を検索し、先読み(?=...)を使用して文字以外を検索します。逆も同様です。

2
andrefs

Unicodeを使用しているとき、\bが本当に変だと気づきました。

/\bo/.test("pop"); // false (obviously)
/\bä/.test("päp"); // true (what..?)

/\Bo/.test("pop"); // true
/\Bä/.test("päp"); // false (what..?)

\b\Bの意味は逆になっているようですが、ASCII以外のUnicodeで使用した場合のみですか?ここでもっと深いことが起こっているかもしれませんが、それが何かはわかりません。

いずれにせよ、Unicode文字自体ではなく、Wordの境界が問題であるようです。おそらく、\b(^|[\s\\/-_&])に置き換えてください。正しく機能しているようです。 (ただし、シンボルのリストを自分よりも包括的にしてください。)

2
apsillers

私のアイデアは、フィンランドの文字を表すコードで検索することです

new RegExp("\\b"+asciiOnly(searchterm), "gi").test(asciiOnly(title))

私の元々の考えは、単純なencodeURIを使用することでしたが、%記号は正規表現を妨害するように見えました。

http://jsfiddle.net/7TsxB/5/

私は、encodeURIを使用して、128を超えるコードですべての文字をエンコードする大まかな関数を記述しましたが、その%を削除し、最初に「QQ」を追加しました。それは最高のマーカーではありませんが、英数字以外を機能させることができませんでした。

1
Heitor Chang

探しているのは、Unicodeの単語境界標準です。

http://unicode.org/reports/tr29/tr29-9.html#Word_Boundaries

ここにJavaScript実装があります(unciodejs.wordbreak.js)

https://github.com/wikimedia/unicodejs

0
Ed.

質問に対する正しい答えは、andrefsによって与えられます。必要なものをすべてまとめた上で、より明確に書き直します。

ASCII textの場合、_\b_を使用して、パターンの先頭と末尾の両方でWordの境界を照合できます。Unicodeテキストを使用する場合、2つの異なるパターンを使用して、同じことをしています:

  • メインパターンの前の開始境界またはワード境界を照合するには、_(?<=^|\P{L})_を使用します。
  • メインパターンの後の末尾またはWord境界に一致させるには、_(?=\P{L}|$)_を使用します。
  • さらに、すべての先頭で_(?i)_を使用して、すべての一致で大文字と小文字を区別しないようにします。

したがって、結果の答えは_(?i)(?<=^|\P{L})xxx(?=\P{L}|$)_です。ここで、xxxはメインパターンです。これは、ASCIIテキストの_(?i)\bxxx\b_と同等です。

コードを機能させるには、次のことを行う必要があります。

  • 変数「searchterm」に、検索するパターンまたは単語を割り当てます。
  • 変数の内容をエスケープします。たとえば、_'\'_を_'\\'_に置き換えます。また、_'\^', '\$', '\/'_などの正規表現の予約済みの特殊文字についても同様にします。質問については here を確認してくださいこれを行う方法。
  • string.replace()メソッドを使用するだけで、変数の内容を上記のパターンの「xxx」の場所に挿入します。
0
Manthos

同様の問題がありましたが、一連の用語を置き換える必要がありました。テキストに2つの用語が隣接している場合(境界が重複しているため)、私が見つけたすべての解決策は機能しませんでした。だから私は少し修正されたアプローチを使わなければなりませんでした:

var text = "Ještě. že; \"už\" à. Fürs, 'anlässlich' že že že.";
var terms = ["à","anlässlich","Fürs","už","Ještě", "že"];
var replaced = [];
var order = 0;
for (i = 0; i < terms.length; i++) {
    terms[i] = "(^\|[ \n\r\t.,;'\"\+!?-])(" + terms[i] + ")([ \n\r\t.,;'\"\+!?-]+\|$)";
}
var re = new RegExp(terms.join("|"), "");
while (true) {
    var replacedString = "";
    text = text.replace(re, function replacer(match){
        var beginning = match.match("^[ \n\r\t.,;'\"\+!?-]+");
        if (beginning == null) beginning = "";
        var ending = match.match("[ \n\r\t.,;'\"\+!?-]+$");
        if (ending == null) ending = "";
        replacedString = match.replace(beginning,"");
        replacedString = replacedString.replace(ending,"");
        replaced.Push(replacedString);
        return beginning+"{{"+order+"}}"+ending;
    });
if (replacedString == "") break;
order += 1;
}

フィドルのコードを参照してください: http://jsfiddle.net/antoninslejska/bvbLpdos/1/

正規表現は以下に触発されています: http://breakthebit.org/post/3446894238/Word-boundaries-in-javascripts-regular

私はソリューションがエレガントだとは言えません...

0