2022年10月14日

集合と範囲 [...]

角括弧 […] 内の複数の文字または文字クラスは、「指定された文字のいずれかを検索する」ことを意味します。

集合

たとえば、[eao] は、3 つの文字 'a''e'、または 'o' のいずれかを意味します。

これは集合と呼ばれます。集合は、通常の文字とともに正規表現で使用できます。

// find [t or m], and then "op"
alert( "Mop top".match(/[tm]op/gi) ); // "Mop", "top"

集合内に複数の文字がある場合でも、それらはマッチ内で正確に 1 つの文字に対応することに注意してください。

したがって、以下の例では一致するものはありません。

// find "V", then [o or i], then "la"
alert( "Voila".match(/V[oi]la/) ); // null, no matches

パターンは以下を検索します。

  • V,
  • 次に、文字 [oi]いずれか
  • 次に、la

したがって、Vola または Vila の一致があります。

範囲

角括弧には、文字範囲を含めることもできます。

たとえば、[a-z]a から z の範囲の文字であり、[0-5]0 から 5 の数字です。

以下の例では、"x" の後に 2 つの数字または A から F までの文字が続くものを検索しています。

alert( "Exception 0xAF".match(/x[0-9A-F][0-9A-F]/g) ); // xAF

ここで、[0-9A-F] には 2 つの範囲があります。これは、0 から 9 までの数字、または A から F までの文字のいずれかの文字を検索します。

小文字の文字も検索したい場合は、範囲 a-f を追加できます: [0-9A-Fa-f]。または、フラグ i を追加します。

[…] 内で文字クラスを使用することもできます。

たとえば、単語の文字 \w またはハイフン - を検索したい場合、集合は [\w-] です。

複数のクラスを組み合わせることもできます。たとえば、[\s\d] は「空白文字または数字」を意味します。

文字クラスは、特定の文字セットの短縮形です。

たとえば、

  • \d – は [0-9] と同じです。
  • \w – は [a-zA-Z0-9_] と同じです。
  • \s – は [\t\n\v\f\r ] と同じで、他にいくつかのまれな Unicode 空白文字も含まれます。

例: 多言語の \w

文字クラス \w[a-zA-Z0-9_] の短縮形であるため、漢字、キリル文字などを見つけることはできません。

任意の言語の単語の文字を検索する、より普遍的なパターンを記述できます。これは Unicode プロパティを使用すると簡単です: [\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]

これを解読しましょう。\w と同様に、次の Unicode プロパティを持つ文字を含む独自の集合を作成します。

  • Alphabetic (Alpha) – 文字用。
  • Mark (M) – アクセント記号用。
  • Decimal_Number (Nd) – 数字用。
  • Connector_Punctuation (Pc) – アンダースコア '_' および同様の文字用。
  • Join_Control (Join_C) – アラビア語などの合字で使用される 2 つの特殊コード 200c および 200d

使用例

let regexp = /[\p{Alpha}\p{M}\p{Nd}\p{Pc}\p{Join_C}]/gu;

let str = `Hi 你好 12`;

// finds all letters and digits:
alert( str.match(regexp) ); // H,i,你,好,1,2

もちろん、このパターンを編集して、Unicode プロパティを追加または削除できます。Unicode プロパティの詳細については、Unicode: flag "u" and class \p{...} の記事を参照してください。

Unicode プロパティは IE でサポートされていません

Unicode プロパティ p{…} は IE では実装されていません。本当に必要な場合は、XRegExp ライブラリを使用できます。

または、興味のある言語の文字範囲 (たとえば、キリル文字の場合は [а-я]) を使用します。

範囲の除外

通常の範囲に加えて、[^…] のように見える「除外」範囲があります。

それらは先頭のキャレット文字 ^ で示され、指定された文字を除く任意の文字に一致します。

たとえば、

  • [^aeyo]'a''e''y'、または 'o' を除く任意の文字。
  • [^0-9] – 数字を除く任意の文字。これは \D と同じです。
  • [^\s] – 空白以外の任意の文字。これは \S と同じです。

以下の例では、文字、数字、スペースを除く任意の文字を検索します。

alert( "alice15@gmail.com".match(/[^\d\sA-Z]/gi) ); // @ and .

[…] でのエスケープ

通常、特殊文字を正確に検索したい場合は、\. のようにエスケープする必要があります。バックスラッシュが必要な場合は、\\ を使用します。

角括弧内では、エスケープせずにほとんどの特殊文字を使用できます。

  • 記号 . + ( ) はエスケープする必要はありません。
  • ハイフン - は、先頭または末尾 (範囲を定義しない場合) ではエスケープされません。
  • キャレット ^ は、先頭 (除外を意味する場合) でのみエスケープされます。
  • 閉じ角括弧 ] は、(その記号を検索する必要がある場合) 常にエスケープされます。

言い換えれば、すべての特殊文字は、角括弧に対して何らかの意味を持つ場合を除き、エスケープなしで許可されます。

角括弧内のドット . は、単なるドットを意味します。パターン [.,] は、ドットまたはコンマのいずれかの文字を検索します。

以下の例では、正規表現 [-().^+] は、文字 -().^+ のいずれかを検索します。

// No need to escape
let regexp = /[-().^+]/g;

alert( "1 + 2 - 3".match(regexp) ); // Matches +, -

…しかし、「念のため」エスケープすることに決めたとしても、害はありません。

// Escaped everything
let regexp = /[\-\(\)\.\^\+]/g;

alert( "1 + 2 - 3".match(regexp) ); // also works: +, -

範囲とフラグ "u"

集合にサロゲートペアがある場合は、それらを正しく動作させるためにフラグ u が必要です。

たとえば、文字列 𝒳[𝒳𝒴] を検索しましょう。

alert( '𝒳'.match(/[𝒳𝒴]/) ); // shows a strange character, like [?]
// (the search was performed incorrectly, half-character returned)

正規表現はデフォルトでサロゲートペアについて「認識していない」ため、結果は正しくありません。

正規表現エンジンは、[𝒳𝒴] を 2 つではなく 4 つの文字であると考えています。

  1. 𝒳 の左半分 (1)
  2. 𝒳 の右半分 (2)
  3. 𝒴 の左半分 (3)
  4. 𝒴 の右半分 (4)

次のようにコードを確認できます。

for(let i=0; i<'𝒳𝒴'.length; i++) {
  alert('𝒳𝒴'.charCodeAt(i)); // 55349, 56499, 55349, 56500
};

したがって、上記の例では、𝒳 の左半分を見つけて表示します。

フラグ u を追加すると、動作は正しくなります。

alert( '𝒳'.match(/[𝒳𝒴]/u) ); // 𝒳

同様の状況は、[𝒳-𝒴] などの範囲を検索する場合にも発生します。

フラグ u を追加するのを忘れると、エラーが発生します。

'𝒳'.match(/[𝒳-𝒴]/); // Error: Invalid regular expression

理由は、フラグ u がないと、サロゲートペアが 2 つの文字として認識されるため、[𝒳-𝒴][<55349><56499>-<55349><56500>] として解釈されるためです (すべてのサロゲートペアがそのコードに置き換えられます)。これで、範囲 56499-55349 が無効であることが簡単にわかります。開始コード 56499 が終了コード 55349 よりも大きいためです。それがエラーの正式な理由です。

フラグ u を使用すると、パターンは正しく動作します。

// look for characters from 𝒳 to 𝒵
alert( '𝒴'.match(/[𝒳-𝒵]/u) ); // 𝒴

課題

正規表現 /Java[^script]/ があります。

文字列 Java に何か一致しますか?文字列 JavaScript には?

答え: いいえ、はい

  • スクリプト Java では、何も一致しません。なぜなら、[^script] は「指定された文字を除く任意の文字」を意味するからです。したがって、正規表現は、その後にそのような記号が続く "Java" を検索しますが、文字列の終わりであり、その後に記号はありません。

    alert( "Java".match(/Java[^script]/) ); // null
  • はい、[^script] の部分が文字 "S" に一致するためです。これは script のいずれでもありません。正規表現は大文字と小文字を区別するため (フラグ i なし)、"S""s" とは異なる文字として扱われます。

    alert( "JavaScript".match(/Java[^script]/) ); // "JavaS"

時間は hours:minutes または hours-minutes の形式にすることができます。時間と分は両方とも 2 桁です: 09:00 または 21-30

時間を見つけるための正規表現を記述します。

let regexp = /your regexp/g;
alert( "Breakfast at 09:00. Dinner at 21-30".match(regexp) ); // 09:00, 21-30

追伸 この課題では、時間が常に正しいと仮定し、"45:67" のような不正な文字列をフィルタリングする必要はありません。後でそれにも対処します。

答え: \d\d[-:]\d\d

let regexp = /\d\d[-:]\d\d/g;
alert( "Breakfast at 09:00. Dinner at 21-30".match(regexp) ); // 09:00, 21-30

ダッシュ '-' は角括弧内で特別な意味を持ちますが、他の文字の間にある場合にのみで、先頭または末尾にある場合はそうでないため、エスケープする必要はありません。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善のための提案がある場合は、コメントする代わりに GitHub の課題を送信するか、プルリクエストを送信してください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • コードを数語挿入するには <code> タグを使用し、複数行の場合は <pre> タグでラップし、10 行を超える場合はサンドボックスを使用してください (plnkrjsbincodepen…)