カテゴリー
【Awk & Jq活用講座】文字列の後方参照を理解する
※ 当ページには【広告/PR】を含む場合があります。
2021/03/31
今回はcsvデータのセル文字列の後方参照をAwkとJqの2つのパターンでどう実現するのかを考えてみるちょっとした技術記事です。
はじめに
当サイトではオフィス業務のComputer-Aidedなハイブリッドな方法を模索し、より効率的なExcel業務を実現したい多忙なオフィスワーカー向けの主にAwkとSedを使うシェル講座です。
シェルスクリプトはどこでもどんなOSでも基本的に使えて、しかも一度使い方を覚えると、Excelと組み合わせて最高に効率の良いオフィスワークツールが作れることでしょう。

正規表現の後方参照とは
どの言語にも正規表現の機能の一部として、検索パターンで対象の文字列内でマッチするかどうかを調べた後に、そのマッチした部分文字列をキャプチャすることが可能です。
キャプチャした部分文字列を後処理から参照する仕組みが
「後方参照」
例えば顧客データベースとして管理しているCsvファイルの中に、以下のようなクレジットカード番号があるとします。
1234-5678-8765-4321
クレジットカード番号は見ての通りで、4桁の数字が4セットで構成されています。
例えばウェブ決済などのサービスで良く目にするのが、セキュリティー強化のための本人確認に使用する下4桁の数字ですが、この例でいうと
4321
Awkの場合
Awk(Gawk)で正規表現による後方参照を行うには
gensub関数
gensub関数については
一般に、正規表現を
(パターン)
Awkでこの後方参照を行うには、
gensub
man awk
gensub(r, s, h [, t])
正規表現rにマッチする対象文字列tを探します。
もし、オプションhが'g'か'G'ならば、rでマッチした全ての箇所をsで置換します。
また、オプションhが数字で指定される場合には、
指定されている番号目にマッチした箇所をsで置き換えます。
対象文字列tが未指定の場合には$0に保持されている文字列が代替されます。
置き換え文字列sで置き換わる位置は、シークエンス\nで与えられ、
この数字nは0から9までの一桁の数字となります。
このシークエンス\nは丸括弧で包んでキャプチャしたマッチング箇所を示すことにも利用できます。
また\0はマッチしたテキスト全てを返し、&文字で結合されています。
sub関数やgsub関数とは違い、元の対象文字列を変化させずに、
関数の返り値として結果を返すことに注意してください。
という感じの関数です。
ではサクッと具体例で確認してみましょう。
以下ではクレジットカード番号(2列目)から4桁情報を抜き出すための操作をgensubで行っています。
$ awk -F"," 'BEGIN { OFS="," } {
last4digits_ = gensub(/[0-9]{4}-[0-9]{4}-[0-9]{4}-([0-9]{4})/, "\\1", g, $2);
print $1, last4digits_;
}' << EOF
グルヤマ マムオ,1111-2222-3333-4444,000
ヌルタニ ポウスケ,5555-5555-6666-6666,222
ポロクチ ニャルミ,3333-4444-5555-9999,777
EOF
#👇出力
グルヤマ マムオ,4444
ヌルタニ ポウスケ,6666
ポロクチ ニャルミ,9999
このスクリプトを解説すると、csvデータの2列目($2)が置換される対象文字列としてgensub関数に置換されるのですが、この文字列は正規表現中の
(...)
"\\1"
jqの場合
Jqでは処理した結果を評価式(ラムダ)的に繋いでいくように処理する独特なスクリプトで記述します。
なので文字列の後方参照というどストレートな機能は無いものの、
使い方はマニュアルの通りで、
用法:
capture(パターン)かcapture(パターン; フラグ)
この関数はJSONオブジェクトからパターン検索で得られたキャプチャに名前を付けて、
そのマッチング結果を格納します。
キャプチャする際に指定した名前はキー値として利用されます。
例:
jq 'capture("(?<a>[a-z]+)-(?<n>[0-9]+)")'
...>
入力:"xyzzy-14"
出力:{ "a": "xyzzy", "n": "14" }
ということでこのcapture関数を使って、先程Awkで行ったスクリプト相当のプログラムをJqでも行ってみます。
$ jq -sR '[ split("\n")[] | select(length > 0) | split(",") ] |
map({
"名義": .[0],
"下4桁": .[1] | capture("[0-9]{4}-[0-9]{4}-[0-9]{4}-(?<digits>[0-9]{4})") | .digits
})
' << EOF
グルヤマ マムオ,1111-2222-3333-4444,000
ヌルタニ ポウスケ,5555-5555-6666-6666,222
ポロクチ ニャルミ,3333-4444-5555-9999,777
EOF
#👇出力
[
{
"名義": "グルヤマ マムオ",
"下4桁": "4444"
},
{
"名義": "ヌルタニ ポウスケ",
"下4桁": "6666"
},
{
"名義": "ポロクチ ニャルミ",
"下4桁": "9999"
}
]
captureを使うことでJqでも簡単に正規表現の後方参照が可能であることが分かります。
なお、csv形式をJsonの2次元配列形式にして返すお約束の部分`
まとめ
今回はAwkとJqで正規表現を使って後方参照をどのように扱うかを見てきました。
結論としては、Awkではgensub関数、Jqではcapture関数をそれぞれ利用すると簡単に後方参照できるということを理解していただけたかと思います。
Csvファイルでデータを捌く場合には両方共とても重宝するので、また別の記事でサラッと今回のテクニックが出現することもあるかと思いますが、そのときにはこの記事の内容を思い出してみてください。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー