範囲指定、行指定、行番号で日本語文書を扱う。perl 1 liner(ワンライナー)

テキスト整形:: Perl でちょろく範囲指定、範囲を置換加工、そのバリエーション 

🐪 Perl使いだして25年、相変わらずタコユーザーですが、日常よく使うパターンを五月雨式に投稿しております。すごいユーザーさんたちは何でもわかるのでしょうが、ふつうなので、誰にでもわかるように説明していきたいと思います。数学は理学部数学科よりもややできなかった人が教えたほうがいいからと思うのです。

さて、sedより便利。PCREはなんだかんだといって標準。自分の使い方ではPythonの正規表現との違いはほんの数カ所だった気がします。なのでPython使っても違和感はないです。sedは挙動がほんの少し異なるので、いまどきのLinuxやMACならPerlで書いたほうが、同じに動作しますから楽ちんです。Perlは派生品がないから基本的に同じ動作をするためですね。

新規端末。CTRL+ALT+T

#10行目から15行目までを出力

まずテストファイル2つを作ります。次のコマンドまたは環境に応じたパスでOK。
だいたい42行のファイルが標準構成で出力されると思います。Perl標準モジュールなのでたぶん名前の重複はないです。末尾に実際のファイルを添付しておきます。

ナンバリングなし。
find /usr/lib/perl5 -type f -name '*x*pm' -exec basename {} \; >text.txt
ナンバリングあり。
find /usr/lib/perl5 -type f -name '*x*pm' -exec basename {} \; | cat -n >text_n.txt 

実践テスト

好きな方/両方で試してみると良いです。

perl -lne 'if ( 10 .. 15 ) {print}'  text.txt
perl -lne 'if ( 10 .. 15 ) {print}'  text_n.txt

これだけで便利さはご理解いただけるかと。

応用 範囲内の文頭のみ置換
perl -lne 'if ( 10 .. 15 ) {s/^/文頭置換/; print}'  text.txt

範囲内の文頭のみ置換し、範囲外はそのまま出力(あえてクドく。範囲内と範囲外での処理を変えてみる場合)
perl -lne 'if ( 28 .. 40 ) {s/^/文頭置換/; print}else{print}'  text.txt 


#Perl 範囲指定でおぼえておくべきこと。Perlの癖みたいなもの。記憶法

  1. . ドットひとつは前後の文字列を連結(この投稿とは無関係の基礎知識)
  2. .. は範囲演算子
  3. ... も範囲、同一行マッチ(同一行の2番目以降のマッチ)はスルーする。あまり知られていない。
そのせいか、不毛なサンプルコードが検索されてしまいます。
基礎であるPerlDocを職業プログラマーでも読まない人が多すぎるからでしょうね。
3つまとめて覚えるとよいです。特に3番めはきっと役に立ちます。これを知らないと10行ぐらい余計なコードを書くはめになります。そして動かないとか。たぶん。

数字で行番号、// で囲えば正規表現で範囲指定できます。
行番号指定との組み合わせも可能です。
/\Q( 文字列 )\E/ で囲えばメタ文字をリテラル文字としてそのまま使えます。
クォートの略でしょうね。全文字列をふつうの文字として扱う際は、
最初\Qだけ必要で
最期の\Eは付けなくてもだいじょうな、ものぐさ仕様です。
サンプルの数字10,15は、お気づきでしょうが行数です。
Perlの特殊変数としては、「$.」 になります。 ワンライナーで
なくeval 使うような場合にはおぼえておくと捗ります。

以上程度の知識と応用で、だいたいのニーズには対応できるかと思います。

#正規表現で範囲出力、応用

perl -lne 'if ( /Tk/ .. /Camel/ ) {print}'  text_n.txt

範囲内の文頭のみ置換 正規表現と行番号のコンビ
perl -lne 'if ( /Exporter\.pm/ .. 15 ) {s/^/文頭置換/; print}'  text_n.txt

結果的に2行目から15行目まで置換して出力します。

範囲内の文頭のみ置換し、範囲外はそのまま出力(あえてクドく。範囲内と範囲外での処理を変えてみる場合) 1ライナーに改行をいれて掲載しています。

perl -lne '
  if ( /オレンジ/ .. /グレープ.*?ス$/ )  {
  s/^/文頭置換/;
  print;
  }
  else{
   print;
}
'  text.txt

実行ファイル(スクリプト)にしてもたったの10行。
GNU nano 2.9.8                                               tikan.plx
#!/usr/bin/perl -ln
if ( /Exporter\.pm/ .. 15 ) {
    s/^/文頭置換/;
    print;
}
else{
    print;
}
__END__


$ tikan.plx text.txt
などとして実行属性つけて叩いてみてください。同じ結果が得られます。

長くなりすぎましたが、ついでに::

結局はPerlでGrepする構文と同じと考えてもいいです。


$ perl -lne 'if ( /xpm$/ ) { print ;$i++}END{print $i . " 個あります"}' text_n.txt

こうすると、リストから xpm だけ抜き出して、その個数を教えてくれます。数える作業なんて人間がやるもんじゃないですから。

さらに簡潔っぽく & 人間らしく。

perl -lne ' /xpm$/ and  print and $i++ ; END{print $i .  "  個あります"}'  text_n.txt

読みこなし:: これもれっきとした制御構造です。最近確信しまた。こういう簡便な書き方で改修が捗るなら使えばいい。ラクダ本にもあります。カンタンなことはカンタンに!!

アンドとORさえ知っていれば英語はOKです。職業的な使い手ではなく道具としてプログラミングツールと向き合うひと向けですかね。

全般にこの書き方だと 単純構造なら、if や 1文字で実現できるお得なwhile を明示的に使わずに短く書けます。複雑になると and/orが複数回登場します。そうなると一般制御構造で書いたほうが圧倒的に人間的です。というかアメリカ人+州立大学で高等数学を教えているような先生でも読めない醜い(=読む気になれない)コードになります。
  1. 1行(1名)づつ地道に処理する前提です。 パール委員長のデフォルトな振る舞い。
  2. /正規表現RE/ に一致したら、その行を出力するだけのカンタンなお仕事。 
  3. ついで、カウンターを1個増やす。(パール委員長が「正」の字を黒板にかいていくイメージです)
  4. END{最期にまとめて「正」字の画数合計を委員長がお知らせ}

手抜きと短さが重要なのでカウンター初期化は省略。正しく動けば正義。初期値が未定義だとゼロなのでいいのです。このサンプルの応用でテキスト処理では相当高度なことはできちゃうと思いますよ。当職の脳内雛型000番の紹介でした。あと99個投稿します。

テストに用いたファイルの生成とリスト

find /usr/lib/perl5 -type f -name '*x*pm' -exec basename {} \;  | cat -n > text_n.txt

     1 ProxyObject.pm
     2 Exporter.pm
     3 Libproxy.pm
     4 wintext.xpm
     5 TextUndo.pm
     6 TextEdit.pm
     7 Text.pm
     8 QuitPBa.xpm
     9 QuitPB.xpm
    10 ned.xpm
    11 file.xpm
    12 Reindex.pm
    13 TextList.pm
    14 FBox.pm
    15 Spinbox.pm
    16 Tk.xpm
    17 ReindexedROText.pm
    18 ColorEdit.xpm
    19 srcfile.xpm
    20 act_folder.xpm
    21 Listbox.pm
    22 TixGrid.pm
    23 Camel.xpm
    24 MsgBox.pm
    25 openfolder.xpm
    26 winfolder.xpm
    27 folder.xpm
    28 DialogBox.pm
    29 ReindexedText.pm
    30 Pixmap.pm
    31 ROText.pm
    32 textfile.xpm
    33 LinkExtor.pm
    34 Proxy.pm
    35 ExampleP.pm
    36 Execute.pm
    37 ProxyServer.pm
    38 gettext.pm
    39 XPathContext.pm
    40 Expat.pm
    41 Showlex.pm
    42 Unix.pm