[ ↑ INDEX ] [ ← PREV ] [ NEXT → ]

Lesson 3 文字列の加工


■0. 準備

cat コマンドで script 内にある kwic1.pl の内容を確認する。

   % cat script/kwic1.pl

次に,voa/* などを対象に,kwic1.pl を実行し結果がどうなるか確認する。

   % perl script/kwic1.pl voa/*

■1. マッチング,置換の対象を指定する

演算子 =~ を使うと指定した変数を対象にマッチングを行うことができる。=~ の否定 (「〜にマッチしない」) も !~ で表わすことができる。

   $var =~ /文字列/     #「文字列」にマッチしたとき真となる
   $var !~ /文字列/     #「文字列」にマッチしなかったとき真となる

マッチングを行う対象を明示しない場合,$_ が対象となるため,これまでは処理対象を明示してこなかったが,明示的に示すと以下のようになる。

   $_ =~ /文字列/

区切り文字が / なら m 演算子は省略可能なので,次の4つは同じことを表わすことになる。

   $_ =~ m/文字列/;      m/文字列/;

   $_ =~  /文字列/;       /文字列/;

s, tr (y) 演算子の場合も =~ で対象を指定できる。置換が成功すると,対象の変数の内容が書き換えられる。

   $var =~ s/文字列/置換文字列/

書き換えたくないもの,書き換えられないものに手を加える場合には,新しい変数に内容をコピーし,その変数を対象に置換を行うとよい。

   #レコードの大文字をすべて小文字に置き換えたものを用意する
   $line = $_; $line =~ tr/A-Z/a-z/;

   #レコードからタグを取り除いたものを用意する (利用例)
   $no_tags = $_; $no_tags =~ s/<[^>]*>//g;

   #$ARGV からパスを取り除き,ファイル名だけを取得する
   $file = $ARGV; $file =~ s#.*/([^/]+)$#$1#;

◇ スクリプトの実行

data ディレクトリにある nihongoA0.cha, nihongoB0.cha は日本語学習者の作文を CHATフォーマットで入力したものである。(フォーマットについて) insertNC.pl はこれらのファイルを対象に,訂正がない文の直後に "%ntv:[タブ](訂正なし)" という行を加えるスクリプトである。スクリプトを実行し結果を確認してから,スクリプトの内容を確認しなさい。

   % less data/nihongoA0.cha
   % perl script/insertNC.pl data/nihongoA0.cha

   script/insertNC.pl
   --------------------------------------------------
   while (<>) {
      if ($prev =~ /^\*/ and $_ =~ /^(\*|\@End)/) {
          print "\%ntv:\t(訂正なし)\n";
      }
      print;
      $prev = $_;
   }
   exit;
   --------------------------------------------------
                                               [解説]

■2. 文字列関数・演算子

以下の関数の中には,文字単位とバイト単位の処理で振る舞いが変わるものがある。これらの違いは,ASCII に含まれない文字を使用する言語 (フランス語,ロシア語,日本語,中国語など) の処理において重要となってくるが,詳しくは Part III で扱うこととし,ここではバイト単位の処理を前提とする。ASCII テキストの場合は1文字1バイト。

関数

chop 対象文字列
文字列の最後の一文字を削除する。対象文字列を省略すると $_ が対象となる。
   $var = "they"; chop $var; print $var;    # the と出力
chomp 対象文字列
文字列末のレコードの区切り文字 (デフォルトは [改行]) を削除する。対象文字列を省略すると $_ が対象となる。デフォルトでは,<> で読み込んだデータには [改行] が付いているので,他の処理を行う前に chomp を使って [改行] を削除しておくことが多い。
lc 対象文字列
uc 対象文字列
対象文字列を小文字 (lc)/大文字 (uc) に変換して返す関数。対象文字列を省略すると $_ が対象となる。chopchomp と違って,引数の文字列そのものを書き換えるわけではない。
   $lower_case = lc $_     #$_ の値を小文字に変換し,$lower_case に代入
   $data = lc $data        #$data の値を 〃 ,$data の新しい値とする
                           #cf. $data =~ tr/A-Z/a-z/
length 対象文字列
対象文字列の長さ(バイト数)を返す関数。対象文字列を省略すると $_ が対象となる。[改行] や [タブ] などの制御文字もバイト数に含まれる。
   $len = length;                           #$len には $_ のバイト数が代入される
   $len = length ("John");                  #$len には 4 が代入される
   $name = "John"; $len = length ($name);   #          〃
substr (対象文字列, 開始位置, 長さ)
「対象文字列」の「開始位置」から「長さ」分の文字を取りだす関数。「開始位置」「長さ」はバイト単位で指定。「開始位置」は0から数える。負の数を指定すると,-1 なら最後のバイト (文字),-2 なら最後から2番目と言うように,最後から前に向かって数える。「長さ」を省略すると,「開始位置」から最後までが対象になる。
      c  o  m  p  u  t  e  r
      0  1  2  3  4  5  6  7    ← 前から数えた位置
     -8 -7 -6 -5 -4 -3 -2 -1    ← 後ろから 〃

   $str = substr ("computer", 3, 3);   #$str には "put" が代入される
   $str = substr ("computer", -2);     #$str には "er" が代入される

   $str = substr ($str, -20);          #$str の後ろから20文字目から
                                       #最後までを$str の新しい値とする,
                                       #つまり,後ろから20文字を残し削除する

演算子

. (ピリオド)
文字列連結の演算子。
   $string = $str1 . $str2;      #2つの変数の値を連結し $string に代入
   print $_ . "\n";              #$_ と [改行] を連結したものを出力
x
文字列の繰り返しを表わす演算子。
   $line = "-" x 60;            #ハイフン60個分を $line に代入
   print "*" x $num;            #変数の値に応じた数の * を出力


  代入演算子

  $string .= ";"    #$string = $string . ";" と同じ  
  $string x= 20     #$string = $string x 20  と同じ


◇ スクリプトの実行

次のコマンドを実行して結果を確認しなさい。

   1.  % perl -e '$var = "ABC"; chop $var; print "$var\n";'
   2.  % perl -e '$var = "ABC"; print lc $var, "\n"'
   3.  % perl -e 'print "123" . "456" . "\n";'
   4.  % perl -e 'print "-" x 30 . "\n";'
   5.  % perl -e 'print length "John", "\n";'

■3. 文字列を整形して出力する: printf, sprintf

printf フォーマット, 文字列

「文字列」を指定されたフォーマットで出力する。文字列はコンマで区切って複数指定できる(下で説明)。フォーマットも文字列も,文字列で指定する場合には引用符で括る。変数で指定する場合には不要。

フォーマットは以下の形式で指定する。

   %[フラグ][最小幅].[最大幅][型指定]

とりあえず理解しておくとよいものだけ示すので,さらに詳しくはレファレンスなどを参照すること。

型指定 s 文字列
d 整数 (10進数)
f 浮動小数点 (固定小数点形式)
% % 記号そのもの。つまり,%% でパーセント記号が出力される。
フラグ  -左詰め (何も指定しないと右詰め)
0指定文字幅より文字数が少ないとき,左側を 0 で埋める
最小幅 数字 最低確保する幅。文字列の長さが足りないと,不足分をスペースや0で埋める。
最大幅 数字 文字列 (s) の場合,文字列の最大幅。
数字 (d) の場合,最小表示桁数。
浮動小数点 (f) の場合,小数点以下の桁数。
   printf "%d",    "4.0"      #"4"    と出力
   printf "%03d",  "4.0"      #"004"   〃
   printf "%-3d",  "4.0"      #"4  "   〃
   printf "%5.2f", "4.0"      #" 4.00" 〃
   printf "%s",    "4.0"      #"4.0"   〃

文字列は複数指定できる。コンマで区切って並べる。各文字列は,フォーマット中の対応する位置の書式で出力される。下のように書くと,文字列1は書式1で,文字列2は書式2で出力される。

   printf "... 書式1 ... 書式2 ...", 文字列1, 文字列2

例えば,変数 $number の値を3桁の数字 (上位桁を0で埋めない) で右揃え,変数 $string の値を文字列とし,": " で区切って出力したいのであれば,次のように指定する。

   printf "%3d: %s", $number, $string
sprintf フォーマット, 文字列
「文字列」を指定されたフォーマットで値として返す。
   $formatted = sprintf ("%4d", $number)

◇ スクリプトの実行:書式を変えて出力する

次のコマンドの下線部 (%d) を 1.〜7. の書式に変えて,出力結果がどう変わるか確認しなさい。

   -------------------------------------------
   % perl -e 'printf "%d|\n", "04.0";'
   4|
   -------------------------------------------

   1.  %6d
   2.  %06d
   3.  %6.4d
   4.  %6.4f
   5.  %s
   6.  %6s
   7.  %-6s

■4. KWIC 形式で出力する

以下は,冒頭で実行した kwic1.pl の内容である。簡単ではあるが,これで KWIC 表示ができるようになる。(KWIC < Key Word In Context)

   script/kwic1.pl
   --------------------------------------------------
   #検索文字列の指定
   $string = '\bjust\b';

   #「段落」単位で処理するため RS を空行に
   $/ = "";

   while(<>){

      s/ *\n */ /g;                   #行の連結

      while(/$string/ig){             #$string の内容は上で指定

         $count++;                    #連番を振るため,数を数える

         $key  =  $&;                 #$key にマッチした文字列を代入
         $pre  =  substr ($`, -25);   #前の文脈の最後の25文字分を $pre に代入
         $post =  substr ($', 0, 25); #$post に後ろの文脈を代入

         $file =  $ARGV;              #現在のファイル名を $file に代入
         $file =~ s#.*/([^/]+)$#$1#;  #パスが付いていたら削除

         #書式を指定して出力
         printf "%5d | %-12.12s | %25s|%s|%-25.25s\n",
                 $count, $file, $pre, $key, $post;

      }
   }

   exit;
   --------------------------------------------------

検索文字列はスクリプトに直接書き込んで指定する。テキストエディタでスクリプトファイルを開いて,$string の値を書き換え保存した後,コマンドラインで実行すればよい。「while(/正規表現/ig)」部分で検索文字列を直接指定するようにしてもよいが,頻繁に値を変更するようなものは,実際に処理する箇所では変数を使い,変数の値はスクリプトの最初で指定するようにした方が使い勝手がよい。

上のスクリプトの実行結果をソートする場合には,UNIX の sort コマンドを利用してソートする。各フィールドが "|" で区切られた形になっているので,区切り文字を "|" とし,キーとしたいフィールドを指定すればよい。キーワードと後ろの文脈でソートするには次のようにする。

   % perl script/kwic1.pl voa/* | sort -f -t "|" +3 > sorted.txt

一度ファイルに保存してからソートしてもよい。

   % perl script/kwic1.pl voa/* > result.txt
   % sort -f -t "|" +3 result.txt > sorted.txt

(sort の使い方については,man sort で調べること。)


□ 練習問題

練習問題A:必ずやること。

  1. kwic1.pl$string の値を変え,いろいろな文字列を検索しなさい。

  2. 出力結果を sort を使い,フィールドを指定してソートしなさい。

  3. 書式 (%-25.25s など) を変えて実行し,意図通りの出力結果になっているか確認しなさい。

  4. 出力の書式 (表示する前後の文脈の長さ) も変数で指定し,値を冒頭で指定するように kwic1.pl を書き換えなさい。

練習問題B:余裕があったらやってみよう。

  1. 検索文字列として「任意の語」が指定できれば,テキスト中のすべての語の KWIC 索引を作ることができることになる。サイズの小さなファイルを選び,すべての語を KWIC 表示しなさい。

    大きなファイル,あるいはワイルドカードを使って複数のファイルを指定すると,結果ファイルが巨大なものになってしまうので,十分注意すること。

  2. kwic1.pl では段落を単位としているので,段落先頭の語の前の文脈,段落末の語の後ろの文脈は表示できない。段落を越えた文脈が表示できるよう,スクリプトを書き換えなさい。段落の境界をどう表示するかも考えること。


参考: kwic1a.pl: kwic1.pl$&, $', $` を使わずに書き換えたもの。練習A, B の答えを一部含んだものになっているので,練習問題を解いてから見ること。

[ ↑ INDEX ] [ ← PREV ] [ NEXT → ]