2017-09-19 12 views
2

私の目標は、特定のclass="..."スタイルの<span>タグに女性の名詞(ドイツ語)をラップして強調表示することです。JavaScript RegExp misbehaving

JavaScriptのRegExでは、私は(残念なことに)「単語境界」\bを使用できませんので、私は単語境界として明示的に列挙することによって即興化を余儀なくされています。

私のコードは、(単純化および合理化された)次のようになります。

const wordBoundary = "(^|\\s|$|/|\\?|\\.|\\!|\\)"; 
"Liebe Grüße".replace(
    new RegExp(`${wordBoundary}(Liebe|Grüße)${wordBoundary}`, "g"), 
    `<span class="nounF">$1$2$3</span>` 
); 

しかし、これが唯一の

<span class="nounF">Liebe </span>Grüßeを生産、最初の単語ではなく、秒を強調しています。

"Liebe Grüße".replace(
    /(^|\\s|$|\/|\\?|\\.|\\!|\\)(Liebe|Grüße)(^|\\s|$|\/|\\?|\\.|\\!|\\)/g, 
    `<span class="nounF">$1$2$3</span>` 
); 

私の質問は次のとおりです。すべてが

<span class="nounF">Liebe</span> <span class="nounF">Grüße</span>を生産、期待通りに動作します - I(偶然ほとんどが)もし代わりに、私は正規表現の初期化子を使用しRegExpオブジェクトのことが判明コンソールで

デバッグ2つ折り:

  1. RegExpオブジェクトを作成し、インプレース正規表現初期化子を使用しないで何か間違っていますか? TBH
  2. 私は正規表現の初期化子を使用する必要がある場合 - どのようにそのカスタムwordBoundaryを提供するのですか?あなたは変数が再び${...}であなたが失うことを再利用したときに(あなたのシナリオで)変数wordBoundaryが正しくがバックスラッシュをエスケープ(\\)が含まれているため

    const wordBoundary = "(^|\\\\s|$|/|\\\\?|\\\\.|\\\\!|\\\\)"; 
    

    これは、しかし:

+0

あなたは現在の単語境界記号に満足していますか?もしそうなら、 'new RegExp(\' $ {wordBoundary}(Liebe |Grüße)(?= $ | [/?。!\\ s])\ '、" g ")'を使用してください。または、 '$ {wordBoundary}'を '(?= $ {wordBoundary}) 'に置き換えてください。 –

+0

LiebeとGrüßeの単語を事前に知っていれば、RegExpオブジェクトを作成することができます。 ? – Redu

+0

これははるかに大きいコードの非常に短いバージョンです。 – YePhIcK

答えて

2

まず者は、あなたのワード境界を考えてみましょう。それを書くのは必ずしも最良の方法ではありませんが、うまくいくでしょう。 \\sで既にカバーされているため、末尾のスペースは|\\)である必要はありません>!から逃げる必要はありませんが、怪我をすることはありません。それだけでcatないdogと一致していることを

const wordBoundary = "(^|\\s|$|/|\\?|\\.|\\!|\\)"; 
 

 
console.log(
 
    "cat dog".match(new RegExp(`${wordBoundary}(cat|dog)${wordBoundary}`, 'g')) 
 
);

お知らせ:

はちょうどASCIIを使用する同様の例を考えてみましょう。または、より正確には、最後にスペースを入れて'cat 'と一致します。これが鍵です。スペースは既に一致していますので、dogと一致させようとしたときに再度一致させることはできません。一致は重複できません。この問題を回避するには、スペースが消費されていないことを確認するために肯定先読みを使用したい:

const wordBoundary = "(^|\\s|$|/|\\?|\\.|\\!|\\)"; 
 

 
console.log(
 
    "cat dog".match(new RegExp(`${wordBoundary}(cat|dog)(?=${wordBoundary})`, 'g')) 
 
);

良い、今ではcatdogの両方にマッチです。最初のスペースの一部ではなく、2番目のマッチの一部であるため、スペースが最初の' dog'にあることに注目してください。私はそれがあるべきワード境界がその例で書かれている方法を変更しているが

const wordBoundary = '[\\s/?.!]'; 
 

 
var re = new RegExp(`(^|${wordBoundary})(Liebe|Grüße|Ärztin)(?=${wordBoundary}|$)`, 'g'); 
 

 
console.log(re); 
 

 
// Test cases 
 
[ 
 
    'Liebe Grüße', 
 
    'Liebe asGrüße Liebe Grüße Ärztin Grüße bd', 
 
    'Liebe GrüßeLiebe Grüße Ärztin Grüße bd', 
 
    'Liebe Grüßeas Liebe Grüße Ärztin Grüße bd', 
 
    'Liebe as Grüße Liebe Grüße Ärztin Grüße bd', 
 
    'Liebe Ärztin Grüße', 
 
    'Liebe\nGrüße', 
 
    'Liebe\tGrüße', 
 
    'Liebe?Grüße', 
 
    'Liebe.Grüße', 
 
    'Liebe!Grüße', 
 
    'Liebe/Grüße', 
 
    'Liebe\\Grüße' 
 
].forEach(function(str) { 
 
    console.log(str.replace(re, '$1<b>$2</b>')); 
 
});

:バック、我々はそれをこのような何かを書くことができ、あなたの元の例に物事を取るために

それが質問に書かれたやり方とまったく同じように書くこともうまくいったと指摘した。

これは未解決の問題を残します。なぜ、余分なエスケープが機能するように見えたのですか?ここではそれを証明するのに役立つシンプルな例です:

// This is the same as: 
 
// var re = new RegExp('(\\\\?)(Liebe|Grüße)(\\\\?)', 'g'); 
 

 
var re = /(\\?)(Liebe|Grüße)(\\?)/g; 
 

 
console.log("Liebe Grüße".replace(re, `<b>$1$2$3</b>`)); 
 

 
console.log("LiebeXX Grüße".replace(re, `<b>$1$2$3</b>`)); 
 

 
console.log("Liebe\\Grüße".replace(re, `<b>$1$2$3</b>`));

私はワード境界の大部分を剥がし、ちょうど交代、\\?の重要な部分に残してきました。ダブルスラッシュは単一スラッシュのエスケープシーケンスで、?は 'オプション'修飾子として扱われます。したがって、これはオプションの\と一致します。言い換えれば、単語の境界は空の文字列と非常に喜んで一致します。効果的には、その境界が\文字でない限り、単に単語境界を無視します。

文字列を使用してRegExpを作成するときは、余分な時間をスラッシュでエスケープする必要があります(文字列リテラルでは1回、RegExpでは1回)。しかし、あなたは元の例でそれをやっていました。もう一度エスケープすると(4つのスラッシュがあるように)、「オプションのスラッシュにマッチする」というシナリオに終わるだけです。

+0

ああ、これは正に正しい答えであり、そのようにタグ付けする必要があります。 '[..]'リストを使ったはるかにエレガントで効果的なソリューションはもちろんですが。詳細な説明をありがとう! – dzuremar

+0

詳細な説明をありがとう(私は誰かが私を教育することを望んでいた)。私はJSでうれしい肯定的な先読みをしています(私は多分MDNのどこかで読んだことがありますが、それらはjavascriptでサポートされていません) – YePhIcK

+1

@YePhIcK Lookbehindsはサポートされていません。 – skirtle

0

あなたはバックスラッシュを倍にする必要がありエスケープする(\\はすべて\となり、他の文字をエスケープするようになりました)。 RegExpリテラルはこの問題を完全に回避します。

編集:これは完全に間違っていますが、これを読んでいても正しい答えがわからない場合は、少し時間をとり、なぜ間違っているのか考えてみてください。このが正しくエスケープある他の場所でアサートされたものに

const wordBoundary = "(^|\\s|$|/|\\?|\\.|\\!|\\)"; 

反し:

+2

これが正しいとは思わない。 '$ {...} 'を使用しても、エスケープのレベルは削除されません。私は答えが**私の答えで動作するように見える理由を説明しようとしました。 – skirtle