私は検索を構築しており、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);
}
では、これらのä、öとåの文字をjavascript regexで動作させるにはどうすればよいですか?
私はユニコードコードを使用するべきだと思いますが、どうすればいいですか?それらの文字のコードは次のとおりです:[\ u00C4、\ u00E4、\ u00C5、\ u00E5、\ u00D6、\ u00F6]
=>äÄåÅöÖ
正規表現と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空白文字。
JavaScript RegExの\b
文字クラスは、単純なASCIIエンコーディングでのみ実際に役立ちます。\b
は、\w
と\W
のセットまたは\w
とストリングの先頭または末尾の境界のショートカットコードです。これらは文字セットは、ASCII "Word"文字のみを考慮します。ここで、\w
は[a-zA-Z0-9_]
に等しく、\W
はそのクラスの否定です。
このため、RegEx文字クラスは、実際の言語を処理するのにほとんど役に立ちません。
\s
は、検索用語が空白文字で区切られている場合に限り、目的どおりに機能するはずです。
この質問は古いですが、ユニコード文字を使用した正規表現の境界のより良い解決策を見つけたと思います。 XRegExpライブラリを使用すると、これを拡張する有効な\ b境界を実装できます
XRegExp('(?=^|$|[^\\p{L}])')
結果は4000文字以上になりますが、非常にうまく機能しているようです。
いくつかの説明:(?=)は、開始または終了の境界または非文字のUnicode文字を検索する長さゼロの先読みです。\bは何もキャプチャしないので、最も重要な考えは先読みです。それは単にtrueまたはfalseです。
Unicodeの特定の文字セットを使用する必要がある場合は XRegExp を使用することをお勧めします。このライブラリの作成者は、さまざまな地域の文字セットをマッピングして、さまざまな言語での作業を容易にしました。
\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']
後読み(?<=...)
を使用して文字を検索し、先読み(?=...)
を使用して文字以外を検索します。逆も同様です。
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\\/-_&])
に置き換えてください。正しく機能しているようです。 (ただし、シンボルのリストを自分よりも包括的にしてください。)
私のアイデアは、フィンランドの文字を表すコードで検索することです
new RegExp("\\b"+asciiOnly(searchterm), "gi").test(asciiOnly(title))
私の元々の考えは、単純なencodeURI
を使用することでしたが、%記号は正規表現を妨害するように見えました。
私は、encodeURIを使用して、128を超えるコードですべての文字をエンコードする大まかな関数を記述しましたが、その%を削除し、最初に「QQ」を追加しました。それは最高のマーカーではありませんが、英数字以外を機能させることができませんでした。
探しているのは、Unicodeの単語境界標準です。
http://unicode.org/reports/tr29/tr29-9.html#Word_Boundaries
ここにJavaScript実装があります(unciodejs.wordbreak.js)
質問に対する正しい答えは、andrefsによって与えられます。必要なものをすべてまとめた上で、より明確に書き直します。
ASCII textの場合、_\b
_を使用して、パターンの先頭と末尾の両方でWordの境界を照合できます。Unicodeテキストを使用する場合、2つの異なるパターンを使用して、同じことをしています:
(?<=^|\P{L})
_を使用します。(?=\P{L}|$)
_を使用します。(?i)
_を使用して、すべての一致で大文字と小文字を区別しないようにします。したがって、結果の答えは_(?i)(?<=^|\P{L})xxx(?=\P{L}|$)
_です。ここで、xxxはメインパターンです。これは、ASCIIテキストの_(?i)\bxxx\b
_と同等です。
コードを機能させるには、次のことを行う必要があります。
'\'
_を_'\\'
_に置き換えます。また、_'\^', '\$', '\/'
_などの正規表現の予約済みの特殊文字についても同様にします。質問については here を確認してくださいこれを行う方法。string.replace()
メソッドを使用するだけで、変数の内容を上記のパターンの「xxx」の場所に挿入します。同様の問題がありましたが、一連の用語を置き換える必要がありました。テキストに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
私はソリューションがエレガントだとは言えません...