📝

Nushellでの正規表現使用方法

2022/12/02に公開

はじめに

Nushellでの正規表現使用方法について説明します。
Nushellの正規表現ですが、Nushell公式マニュアルには以下のみの記述になっています。

Regular expressions in Nushell's commands are handled by the rust-lang/regex crate. If you want to know more, check the crate documentation: "regex".

とさらっとしかかいていない状況です。regex crateはGoogleの高速なRE2にインスパイアされた正規表現のライブラリ(Crate)ですね。2017年の記事ですが、A comparison of regex enginesによれば、なかなか優秀なようです。

特徴的なものとして、(1)named capture groups,(2)Unicode対応です。正規表現については他の書籍などで紹介されているので、割愛します。

Nushellではbuilt-inコマンドのfind, parser, str repalce, where(=~!~オペレータ[1])で正規表現が使えます。

マッチング文字

以降正規表現で使うマッチング文字について説明します。

Unicode文字のプロパティ

Unicodeではコードポイントで直接指定する方法とは別に特性で分類したプロパティーでマッチングする方法があります。

プロパティはProperty_Indexにあるように、たくさんの種別があります。実装はRegexのregex_syntaxやその解説を参考にしてください。正規表現クレートは完全なサポートを提供するわけではありませんが、特に有用な以下のサブセットをカバーしています。

  • General categories
  • Scripts and Script Extensions
  • Age

NushellはUnicode文字のプロパティでマッチングできます。以下は漢字を表すHanを使ったNushellでの実行例。

❯ '人間は動物である.' | parse -r '(\p{Han}+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ 人間     │
│ 2 │ 動物     │
╰───┴──────────╯
❯ '人間はヒトである' | parse -r '(\p{Han}+)(\p{hiragana}+)(\p{katakana}+)(\p{hiragana}+)'
╭───┬──────────┬──────────┬──────────┬──────────╮
│ # │ Capture1 │ Capture2 │ Capture3 │ Capture4 │
├───┼──────────┼──────────┼──────────┼──────────┤
│ 1 │ 人間      │ は       │ ヒト      │ である    │
╰───┴──────────┴──────────┴──────────┴──────────╯

General_Category

デフォルトの分類として使用できるさまざまな文字タイプへの便利な分類です。表音文字,表語文字(Letter,数字(Number),記号(Symbol)といった一般的な文字の分類を表します。

❯ "123abc\(\)" | parse -r '(\p{General_Category=Letter}+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ abc      │
╰───┴──────────╯

Script

正規表現で使われるものです[2]

❯ "アイウーエオ" | parse -r '(\p{Script=Katakana}+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ アイウ   │
│ 2 │ エオ     │
╰───┴──────────╯

Age

Unicode Age propertyに対応しています。\p{Age:6.0}はUnicode 6.0[3]に対応します。

その他

Script_Extensions(正規表現などで使用するスクリプト値の列挙セット。), Block(コード ポイントの範囲のブロック名のリストで構成される), East_Asian_Width(東アジアのコンテキストで幅の広いグリフと幅の狭いグリフの選択を決定するためのプロパティ)などがある。

省略記法

以下に一部のプロパティの省略記法を紹介します。なお、General_CategoryおよびScriptに関しては、プロパティ名を省略できます。

  1. General_Category
    上記リンクに省略形が記載されています。
    例:
'\p{gc=Lo}' # General_Category=Other_Letter
  1. Script
    上記リンクに省略形が記載されています。
    例:
'\p{sc=Kana' # Script=Katakana

単一文字

1つの文字にマッチするものを次に示します。

文字 説明
. 改行を含む可能性のある任意の文字 (s=true)
[xyz] 文字クラス
[^xyz] 否定文字クラス
[a-z] 範囲指定
[x[^xyz]] ネストパターン
[a-y&&xyz] 積集合(この場合はxないしy)
[0-9&&[^4]] 差分(この場合は4を除く0〜9).次と等価 [0-9--4]
\pN Unicode 文字クラス (1 文字の名前)
\p{Greek} Unicode 文字クラス
\PN 否定 Unicode 文字クラス (1 文字の名前)
\P{Greek} 否定 Unicode 文字クラス

Nushell実行例:

❯ "abcdefghijklmnopqrstuvwxyz" | parse -r '([a-y&&xyz]+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ xy       │
╰───┴──────────╯

文字クラス(ASCII character classes)

文字クラスを使ったマッチングの例です。

❯ '123' | parse -r "([[:digit:]])"
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ 1        │
│ 2 │ 2        │
│ 3 │ 3        │
╰───┴──────────╯

文字クラス一覧です。

class 説明
[[:alnum:]] 英字と数字([0-9A-Za-z])
[[:alpha:]] 英字([A-Za-z])
[[:ascii:]] ASCII ([\x00-\x7F])
[[:blank:]] 空白([\t ])
[[:cntrl:]] コントール文字([\x00-\x1F\x7F])
[[:digit:]] 数字 ([0-9])
[[:graph:]] graphical ([!-~])
[[:lower:]] 小文字英字 ([a-z])
[[:print:]] 印字可能文字 ([ -~])
[[:punct:]] 句読点 ([!-/-@[-`{-~:])
[[:space:]] 空白 ([\t\n\v\f\r ])
[[:upper:]] 大文字英字 ([A-Z])
[[:word:]] 英数字 ([0-9A-Za-z_])
[[:xdigit:]] 16進文字([0-9A-Fa-f])

Nushell実行例:

❯ "123abc\(\)" | parse -r '([[:alnum:]])'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ 1        │
│ 2 │ 2        │
│ 3 │ 3        │
│ 4 │ a        │
│ 5 │ b        │
│ 6 │ c        │
╰───┴──────────╯
❯ "123abc\(\)" | parse -r '([[:^alnum:]])'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ (        │
│ 2 │ )        │
╰───┴──────────╯

Perl正規表現文字クラス(Perl character classes)

Perl正規表現文字クラスを次に示す。カッコ内は等価の文字クラス

class 説明
\d 数字 (\p{Nd})
\D 非数字
\s 空白 (\p{White_Space})
\S 非空白
\w 英単語 (\p{Alphabetic} + \p{M} + \d + \p{Pc} + \p{Join_Control})
\W 非英単語

Nushell実行例:

❯ "123abc\(\)世界456def+-平和" | parse -r '(\d+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ 123      │
│ 2 │ 456      │
╰───┴──────────╯
 ❯ "123abc\(\)世界456def+-平和" | parse -r '(\D+)'
╭───┬───────────╮
│ # │ Capture1  │
├───┼───────────┤
│ 1 │ abc()世界 │
│ 2 │ def+-平和 │
╰───┴───────────╯
❯ "123abc\(\)世界456def+-平和" | parse -r '(\d+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ 123      │
│ 2 │ 456      │
╰───┴──────────╯
 ❯ "123abc\(\)世界456def+-平和" | parse -r '(\D+)'
╭───┬───────────╮
│ # │ Capture1  │
├───┼───────────┤
│ 1 │ abc()世界 │
│ 2 │ def+-平和 │
╰───┴───────────╯
❯ "123abc\(\)世界 456def+-平和" | parse -r '(\w+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ 123abc   │
│ 2 │ 世界     │
│ 3 │ 456def   │
│ 4 │ 平和     │
╰───┴──────────╯
 ❯ "123abc\(\)世界 456def+-平和" | parse -r '(\W+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ ()       │
│ 2 │          │
│ 3 │ +-       │
╰───┴──────────╯

エスケープシーケンス(Escape sequences)

エスケープシーケンスを次に示す。

エスケープ文字 説明
* * , 同様に\でエスケープして'.+*?()
\a bell (\x07)
\f フォームフィード(次の論理ページの最初に移動) (\x0C)
\t タブ
\n 改行
\r 行末から行頭に戻す復帰コード
\v 垂直タブ(\x0B)
\123 8進文字コード (up to three digits) (when enabled)
\x7F 16進文字コード (exactly two digits)
\x{10FFFF} ユニバーサル文字名
\u007F ユニバーサル文字名
\u{7F} ユニバーサル文字名
\U0000007F ユニバーサル文字名
\U{7F} ユニバーサル文字名

Nushellではユニバーサル文字はダブルクォートで囲みます。

❯ "\u2615"
☕

合成(Composites)

文字 説明
xy x, yが連続している
x|y xないしy(x 優先)

繰り返し(数量詞)

文字 説明
x* "x" の 0 回以上の繰り返し
x+ "x" の 1 回以上の繰り返し
x? "x" の 0 回か 1 回の出現に一致
x*? ゼロ以上の x、より少ない値を優先
x+? 1 以上の x、より少ない値を優先
x?? 0 または 1 の x、0 を優先
x{n,m} "n" には 0 と正の整数が、 "m" には "n" より大きい正の整数が入ります。直前の項目 "x" が少なくとも "n" 回、多くても "m" 回出現するものに一致
x{n,} "n" には正の整数が入ります。直前の項目 "x" の少なくとも "n" 回の出現に一致
x{n} "n" には正の整数が入ります。直前の項目 "x" がちょうど "n" 回出現するものに一致
x{n,m}? n または n+1、または ... 、または m x、より少ない値を優先
x{n,}? n 以上の x、より少ない値を優先
x{n}? ちょうど n x

Empty matches

マッチ文字 説明
^ 文字列先頭(複数行モードの場合は、行先頭)
$ 文字列終端(複数行モードの場合は、行終端)
\A 文字列先頭(複数行モードの場合は、文字列先頭)
\z 文字列終端(複数行モードの場合は、文字列終端)
\b Unicode word境界(Unicode文字とそれ以外の境界部分でマッチする)
\B 非Unicode word境界

^$の通常モードとマルチラインモードの挙動の違い:

❯ "123" + (char newline) + "abc" | parse -r '^(\d+)$'
❯  "123" + (char newline) + "abc" | parse -r '(?m)^(\d+)$'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ 123      │
╰───┴──────────╯

\b\Bとの違い:

❯ "\u2615abc\u2615def\u2615ghi" | parse -r '(\b\w+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ abc      │
│ 2 │ def      │
│ 3 │ ghi      │
╰───┴──────────╯

 ❯ "\u2615abc\u2615def\u2615ghi" | parse -r '(\B\w+)'
╭───┬──────────╮
│ # │ Capture1 │
├───┼──────────┤
│ 1 │ bc       │
│ 2 │ ef       │
│ 3 │ hi       │
╰───┴──────────╯

Grouping

単一文字でなく、カッコでくくった文字列をグループとして、抽出しやすくします。またその内容を個別に取り出すcaptureという機能があります。

Named capture groups

名前付きcaptureはグループにマッチする文字列に名前をつけて抽出するものです。
Nushellでは名前はテーブルのカラム名になります。(?P<name>\w+)の部分がnamaed captrueです。

❯ echo "foo bar." | parse -r '\s*(?P<name>\w+)(?=\.)'
╭───┬──────╮
│ # │ name │
├───┼──────┤
│ 1 │ bar  │
╰───┴──────╯

Grouping and flags

groupingで使用する表記は以下のとおりです。

文字 説明
(exp) 番号付きキャプチャグループ (サブマッチ)
(?P<name>exp) 名前付きの & 番号付きキャプチャグループ (サブマッチ)
(?:exp) 非キャプチャグループ
(?flags) 現在のグループ内にフラグを設定; 非キャプチャ
(?flags:exp) 再実行中にフラグを設定; 非キャプチャ
フラグ文字 説明
i 大文字と小文字を区別しない: 文字は大文字と小文字の両方に一致します
m マルチライン(複数行)モード^と$は行の先頭と終端にマッチします
s .は改行にもマッチします
U 'x*' と 'x*?'の意味を交換します
u Unicodeサポート(デフォルトで有効)
x 空白を無視、行頭に'#'があるとコメント行として無視します

Nushell実行例:

❯ "ENV = value" | parse -r '([A-Z]+)\s+\=\s+(?i:(\w+))'
╭───┬──────────┬──────────╮
│ # │ Capture1 │ Capture2 │
├───┼──────────┼──────────┤
│ 1 │ ENV      │ value    │
╰───┴──────────┴──────────╯
❯ "ENV = Value" | parse -r '([A-Z]+)\s+\=\s+(?i:(\w+))'
╭───┬──────────┬──────────╮
│ # │ Capture1 │ Capture2 │
├───┼──────────┼──────────┤
│ 1 │ ENV      │ Value    │
╰───┴──────────┴──────────╯

TIPS

Unicode関連Nushell built-inコマンドなど

  • codepointから文字を生成
❯ char -i 0x2615
☕
  • 連結
❯ "abc" + "def"
  • 改行など[4]
❯ (char newline)
  • バイト列からUnicode
❯ 0x[e2 98 95] | decode utf8
☕

Unicode関連ツール

$ uni identify €
     CPoint  Dec    UTF8        HTML       Name (Cat)
'€'  U+20AC  8364   e2 82 ac    &euro;     EURO SIGN (Currency_Symbol)
$ uni i h€ý
     CPoint  Dec    UTF8        HTML       Name (Cat)
'h'  U+0068  104    68          &#x68;     LATIN SMALL LETTER H (Lowercase_Letter)
'€'  U+20AC  8364   e2 82 ac    &euro;     EURO SIGN (Currency_Symbol)
'ý'  U+00FD  253    c3 bd       &yacute;   LATIN SMALL LETTER Y WITH ACUTE (Lowercase_Letter)
$ head -c2 README.markdown | uni i
     cpoint  dec    utf-8       html       name (cat)
'['  U+005B  91     5b          &lsqb;     LEFT SQUARE BRACKET (Open_Punctuation)
'!'  U+0021  33     21          &excl;     EXCLAMATION MARK (Other_Punctuation)
$ uni i -c -f '0x%(hex): "%(keysym)", // %(name)' h€ý
0x68: "h", // LATIN SMALL LETTER H
0x20ac: "EuroSign", // EURO SIGN
0xfd: "yacute", // LATIN SMALL LETTER Y WITH ACUTE
$ uni s globe earth
     CPoint  Dec    UTF8        HTML       Name (Cat)
'🌍' U+1F30D 127757 f0 9f 8c 8d &#x1f30d;  EARTH GLOBE EUROPE-AFRICA (Other_Symbol)
'🌎' U+1F30E 127758 f0 9f 8c 8e &#x1f30e;  EARTH GLOBE AMERICAS (Other_Symbol)
'🌏' U+1F30F 127759 f0 9f 8c 8f &#x1f30f;  EARTH GLOBE ASIA-AUSTRALIA (Other_Symbol)
  • ucd-generate rust:regexはこのツールでUnicode propertyの分類をしている。

さいごに

書いている本人が完全に理解していないので、間違いなどあればご指摘ください。

参考文献

脚注
  1. Perlと同じ ↩︎

  2. Script定義 ↩︎

  3. Unicodeコンソーシアムが策定している文字コード「Unicode」の、2011年2月に新たに公開された仕様 ↩︎

  4. char_s.rs参照 ↩︎

Discussion