Windows版Perlでの行の抽出の問題

このQ&Aのポイント
  • WindowsのバッチファイルでPerlを使用して特定の文字列を含む行を抽出する処理を行っていますが、うまくいかないケースがあります。
  • 検索対象ファイルの文字コードを「Shift-JIS」に設定し、大文字/小文字を問わず「〔a」を含む行を抽出しようとすると、意図しない結果が得られます。
  • また、検索対象ファイルの文字コードを「UTF-8」に設定しても、何も行が抽出されません。findstrコマンドでは望んだ結果を得ることができますが、スクリプトファイルに対応していないため、不便です。問題の原因について教えてください。
回答を見る
  • ベストアンサー

Windows版Perlでの行の抽出の問題

Windowsのバッチファイルで、Perlを使って特定の文字列を含む行を抽出する処理を行っているのですが、うまくいかないケースがあります。 例えば、以下の線内のようなテキストがあって、 ---------------------- 〔a 〔A ゜a ゜A ---------------------- 大文字/小文字を問わず、「〔a」を含む行を抽出し、 以下の線内のような結果を得たいとします。 ---------------------- 〔a 〔A ---------------------- 検索対象ファイルの文字コードが「Shift-JIS」で、 「print if ( /〔a/i );」という構文で行を抽出すると、 以下の結果になってしまいます。 ---------------------- 〔a 〔A ゜a ゜A ---------------------- 「print if ( /〔a/ );」なら以下の結果になってしまいます。 ---------------------- 〔a ---------------------- 検索対象ファイルの文字コードを「UTF-8」にし、 「print if ( /〔a/ );」という構文で、 「〔a」を含む行を抽出しようとすると、何も行が抽出されません。 他のコマンドでの大文字/小文字問わない「〔a」を含む行の抽出は、 findstrでは得たい結果が得られ、 grep、AWKでは、Perl同様、「゜a」を含む行が抽出されてしまいます。 しかし、私が行いたい処理は、数百~数千程度の単語が書かれた単語のリストがあり、そのリストをスクリプト形式に置換して行を検索するので、スクリプトファイルに対応していないfindstrコマンドでは不便なのです。 上記の問題の原因が分かる方がおられましたら、お教え頂けないでしょうか。

  • kon77
  • お礼率97% (92/94)
  • Perl
  • 回答数6
  • ありがとう数16

質問者が選んだベストアンサー

  • ベストアンサー
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.5

すみません, ちょっと確認ですが print if ( /〔a/i ); を print Encode::encode('utf-8', $_) if ( /〔a/i ); にしたら (これでエラーになるなら use Encode; を最初の方に追加して上の Encode::encode をただの encode にする) どうなるでしょうか? 余談ですが, この位置に if をつけるときにはかっこはなくてもよかったりします.

kon77
質問者

お礼

「print if ( /〔a/i );」を「print Encode::encode('utf-8', $_) if ( /〔a/i );」に変えただけなら、「Undefined subroutine &Encode::encode called at script.pl line 7, <> line 1.」というエラーが出ました。 上記に加え、「use Encode;」も記述するとエラーは出なくなりました。大変助かりました。ありがとうございました。 あと、かっこの件ですが、この場合は要らないのですね。勉強になりました。 私はまだPerlの構文を理解していないので、WEBにあったサンプルコードを自分なりに書き換えて使っていまして、使い方を間違っている部分もありますね。お恥ずかしいです。 おかげさまで完璧に行の抽出が出来るようになりました。大変助かりました。ご回答、本当にありがとうございました。

kon77
質問者

補足

お二人とも、大変参考になりました。 ご回答、本当にありがとうございました。

その他の回答 (5)

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.6

> Perlでの置換なら、「 s/[Aa]/[Aa]/gi; s/[Bb]/[Bb]/gi; … s/[Zz]/[Zz]/gi; 」の様な感じで置換するという事です。「あaいi」は「あ[Aa]い[Ii]」と置換されます。 $foo = 'アaイi'; $foo =~ s/[aA]/[aA]/gi; $foo =~ s/[iI]/[iI]/gi; print "$foo\n"; # ゼaA][aA]イ[iI] 確かに「あaいi」ではうまくいきますが、カタカナにした「アaイi」では文字化けしてしまいます。「ア」の2バイト目が大文字の "A" になっているからです。(ちなみに「イ」の2バイト目は "C" ですので、s/[cC]/[cC]/gi; を適用すると文字化けすることになります。) Shift_JIS は厄介な文字コードですので、utf8 または euc-jp で Perl プログラムを書くことができるのでしたら、そちらの方がトラブルに見舞われることが少なく楽をすることができます。(「/〔a/i」や「/あaいi/i」もそのままで実行できると思います。)

kon77
質問者

お礼

再度、ご回答ありがとうございます。 なるほど、つまり、「[aA]」の様にしても意味がないという事ですね。Shift_JIS使うと大変ややこしくなるのですね。大変参考になりました。今後はなるべくUTF-8等を使って行こうと思います。 「〔」と「゜」の問題は、Perlスクリプト内で文字コードをUTF-8にする事で解決出来ました。ありがとうございました。 今回は色々と勉強になりました。長い間付き合って頂き、本当にありがとうございました。大変助かりました。 ご回答、本当にありがとうございました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

現状どんなスクリプトなのかまったくわからんので想像するしかないんだけど, たいてい ファイルから入力する (このときに内部コードに変換する)→なんかする→出力する (このときに内部コードから変換する) でいけると思うんだ. あ, もちろん Perl のバージョンに依存する部分はあるけどね.

kon77
質問者

お礼

再度、ご回答ありがとうございます。 私はPerlの文法が分からないので、スクリプトは、単純に単語のリストをスクリプト形式に書き換えただけのものです。 「りんご」と書かれた行なら、「print if ( /りんご/ );」と置換しているだけです。 それをバッチファイルから「perl -n "script.pl" "in.txt" > "out.txt"」の様な感じで実行しています。 スクリプトの件ですが、色々試してみましたが、エラーは出るものの、以下のようにするとうまくいきました。 -------------------------------- ■ バッチファイルの内容 perl "script.pl" "in.txt" > "out.txt" ■ スクリプトファイル「script.pl」の内容 use strict; use warnings; use utf8; use open IO => ":utf8"; while (<>) { print if ( /〔a/i ); } ■ 入力ファイル「in.txt」の内容 〔a 〔A ゜a ゜A ■ 出力ファイル「out.txt」の内容 〔a 〔A ■ 文字コード in.txt(UTF-8) out.txt(UTF-8) script.pl(UTF-8) ■ エラーメッセージ Wide character in print at script.pl line 7, <> line 1. Wide character in print at script.pl line 7, <> line 2. -------------------------------- これで目的は達成出来ましたので、後は地道に調べてエラーも出来れば消そうと思います。 あと何時間かしたら質問を締め切らせて頂こうと思いますので、それまでにもし何かアドバイス等ございましたら頂けると有り難いです。 ご回答ありがとうございました。

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.3

書くのが面倒と言えば面倒なのですが、(?i) には別の書き方があります。 print if (/あ(?i:a)い(?i:i)/); ?i の直後にコロンを置いて、続けてパターンを書けば外側に影響することはありません。ただ、Shift_JIS の2バイト目には、バックスラッシュや正規表現のメタ文字も含まれるので注意が必要です。

kon77
質問者

お礼

再度、ご回答ありがとうございます。 正規表現で置換コマンドの一文を追加するだけなので、面倒という訳ではないのですが、思い通りの動作をしてくれるかが心配です。 今回お教え頂いた方法を使う場合、Perlの正規表現での置換なら、「s/([a-zA-Z]+)/(?i:\1)/gi;」の様な感じでリストを置換すればいいと思うのですが、「Shift_JIS の2バイト目には、バックスラッシュや正規表現のメタ文字も含まれる」というのが気になります。なんとなくおっしゃっている意味は分かりますが、正直まだ完璧には理解出来ていません。 もう一つ私が考えている方法としましては、あまりスマートな方法ではないかも知れませんが、全てのアルファベットを、「[Aa]」のように置換する方法です。 Perlでの置換なら、「 s/[Aa]/[Aa]/gi; s/[Bb]/[Bb]/gi; … s/[Zz]/[Zz]/gi; 」の様な感じで置換するという事です。「あaいi」は「あ[Aa]い[Ii]」と置換されます。 こちらの方が安全かと思うのですが、同じことなのでしょうか。 つまり、 「print if (/あ(?i:a)い(?i:i)/);」と、 「print if (/あ[Aa]い[Ii]/);」なら どちらがいいのかという事です。同じでしょうか。 とにかく、もう少し調べてみようと思います。ご回答ありがとうございました。

  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.2

入力 (と出力) の際に, 内部コードとの間で文字コードを変換する. 必要であればスクリプトで使っている文字コードも指定する.

kon77
質問者

お礼

ご回答、ありがとうございます。 Perlのスクリプト内に、ファイルからのデータを入出力の際、文字コードを変換する処理を書くということでしょうか。 自分はあまりPerlに詳しくないので、今から調べてやってみようと思います。 出来たらまた追記したいと思います。ご回答ありがとうございました。

kon77
質問者

補足

色々調べてみたのですが、単純にスクリプトに1、2行足せばいいというものではなく、もう少し複雑になる様で、そうなると色々と勉強しなければつまずく箇所が多いので、今回はこちらの方法は見送ろうと思います。 一応、WEBにあったサンプルのコードを、そのままコピペして実行してみたのですが、環境が合っていないのか、エラーが出てうまく行かないので、実現まで時間が掛かりそうなのです。 Perlに関する本は手元にあるので、時間がある時に勉強して、マスターしたら今回の問題を解決しようと思います。 ご回答、ありがとうございました。

  • kumoz
  • ベストアンサー率64% (120/185)
回答No.1

〔a ==> \x81ka 〔A ==> \x81kA ゜a ==> \x81Ka ゜A ==> \x81KA print if (/\x81ka/i); 〔 の文字コードが 81 6B (6B は小文字の k) で、゜の文字コードが 81 4B (4B は大文字の K) のため、i 修飾子を付けるとすべてマッチしてしまうことになります。Shift_JIS の2バイト目にはアルファベットに該当する文字がたくさんあるため、i 修飾子を使うと別の文字にマッチしてしまう危険があります。 パターン修飾子は正規表現の内部にも書くことができ、次のようにすれば 〔a と 〔A のみを抽出できるようになります。 print if (/〔(?i)a/);

kon77
質問者

お礼

ご回答、ありがとうございます。大変よく分かりました。 実際に作業で使うリスト内の単語は様々で、「あaいi」の様に、2バイト文字と半角文字が複数回交互に来る場合もあるので、単語リストをスクリプトに置換する時、「print if (/あ(?i)aいi/);」にすると、2バイト文字の「い」が「(?i)」より後に来てしまいますし、「print if (/あaい(?i)i/);」とすれば「a」が半角のみにマッチしてしまうと思うので、なかなか難しいです。 実際作業に使用するリストの単語は、2文字以上の場合が多いので、今回の件が原因での不具合は起こり辛いのですが、不完全な仕組みで長期間処理を続けて、いつか不具合が起きると嫌なので、出来れば完璧に間違いのない行の抽出をしたいのです。 ただ、ご回答の内容で大変よく分かりましたので、なんとか解決策を見付けられそうです。ご回答、ありがとうございました。

関連するQ&A

  • WindowsでPerlをする際,1行目の"#! ~"はどのように?

    Perlの参考書・本等ではプログラムファイルの第1行目は #! /usr/local/bin/perl という1行がよく有りますが,WindowsXPを用いている場合では,この1行をどう直せばよいのでしょうか? #! (perl.exeが存在するフォルダのパス) でよいのでしょうか? 私はWindowsXPを使っていて,この1行を使わずにプログラムを書いておりましたので,この1行の意味がよく分かりません。 ある参考書には,"#!はその行に書いたコマンドに,ファイルの残りの部分を渡して実行すると言う性質を持っている"と有りました。だから,試しにfile1.plとfile2.txtを準備し, file1.plの中身  #! (perlの存在するフォルダのパス)\perl.exe  while(<STDIN>){   print;  } file2.txtの中身  hello world として,コマンドプロンプトで file1.pl < file2.txt としたのですが正しく動作しませんでした.(perl file1.pl < file2.txt と入力した場合は正しく"hello world"となりました)

    • ベストアンサー
    • Perl
  • perlでcsvファイルから複数行を抽出したい

    プログラミング初心者です。 お知恵をお貸しください。 perlでcsvファイルから任意の複数行を、コマンドライン引数を使って抜き出したいです。 1行だけだとうまくいきますが、以下ではすべてのcsvデータが出力されてしまいます。 ----------------------getcsv.pl ここから---------------------- #!/usr/bin/perl -- use strict; use warnings; open(IN, "<adata.csv"); open (OUT, ">$bdata.csv"); while(<IN>){ if($. == $ARGV[0] || $ARGV[1] || $ARGV[2] || $ARGV[3]) { print OUT $_; } } close(IN); close (OUT); ----------------------getcsv.pl ここまで---------------------- ----------------------コマンド ここから---------------------- perl getcsv.pl 1 3 8 10 ----------------------コマンド ここまで---------------------- よろしくお願いします。

    • ベストアンサー
    • Perl
  • 文字列の抽出がうまくできません

    こんにちは。Perl初心者です。みなさんのお力をお借りしたく、初めて投稿させていただきました。よろしくお願いします。 現在、ファイルから特定した行を抽出し、その行に含まれる文字列を抽出しようとしているのですが、その文字列の抽出がうまくできません。 <文字列を含むファイル:xxx.txt> -------------------------- To 鈴木 太郎さん 1行目 : 01,りんご, 3 個, ナシ, 1個 14行目 02,キャベツ, 3 個, なす, 10本 15行目 : -------------------------- <スクリプトファイル:yyy.pl> -------------------------- open(DAT, "./xxx.txt") || die("can't open file : ($!)"); @file = <DAT>; close DAT; if ( $file[0] =~ /(\W+\s)(\W+)/ ) { print "$1\n"; } print "$file[13]\n"; print "$file[14]\n"; -------------------------- <結果> -------------------------- 鈴木 -------------------------- 抽出したい結果は「鈴木 太郎」なんですが、上記のような結果(「1byteのスペース」鈴木「2byteのスペース」)になってしまいます。xxx.txtの「To」と「鈴木」の間と、「鈴木」と「太郎」の間にはそれぞれ1byteのスペースがあります。どうやったら「鈴木 太郎」と抽出できるか教えてください。 スクリプトは「Copal2(ver2.77)」というツールを使って作りました。そのツールではエンコードは「自動判別」となっており実際何が使われているのか分かりません。 また他にも質問があります。 今抽出したい行を指定して出力させてますが、 「To」から始まる行 「01」といった2桁の数字から始まる行 というような抽出の仕方をしたいのですが可能でしょうか? 本当に初心で大変恐縮ではありますが、教えてください。 よろしくお願いいたします。

    • ベストアンサー
    • Perl
  • Perlのワンライナーをスクリプトにしたい

    初歩的な質問で恐縮ですが、以下のPerlのワンライナーを、スクリプトファイルに記述するには、どのように書けば良いのでしょうか。 perl -ne "print if ( /aaa/ ); print if ( /bbb/ ); print if ( /ccc/ );" "in.txt" > "out.txt" 下記のようにしてみたのですが、ダメでした。 perl "script.pl" "in.txt" > "out.txt" ----- script.plの内容 ----- print if ( /aaa/ ); print if ( /bbb/ ); print if ( /ccc/ ); --------------------------- 環境はWindowsのコマンドプロンプトです。バッチファイルでの使用を想定しています。 自分はPerlに関する知識が無いので、Perlに関するサイトも見てみたのですが、情報が膨大で、ピンポイントでこの問題を解決する情報を探し出す事が出来ませんでした。 やり方が分かる方がおられましたら、お教え頂けないでしょうか。

    • ベストアンサー
    • Perl
  • DOSの文字抽出

    findstrで特定の行を抽出できました。 test.txtというファイルに入っています。 85文字のうちの43行目から8文字抽出したいのですが、どうすればいいでしょうか?

  • 行を指定して削除する方法PERL

    ある文字を検索して、その行を含む&1行前と2行後ろの行までを削除するスクリプトを書きたいのですが、上手くいきません。 検索して行番号を獲得して、 $rowという変数に入れました。 それをさらに $a :1行前 $b :2行後ろの行番号に格納しました。 問題は削除するところが上手く行きません。 next if で $aから$bの行番号を削除して、と頼んでいるのですが、空のファイルに上書きされてしまいます。 お願いです。この方法で何が間違っているかを教えてください! #!/opt/perl/5.8.0/bin/perl -w print "Content-type: text/html\n\n"; use CGI qw(:standard); use CGI::Carp qw/fatalsToBrowser/; $filename = "../XML/link.xml"; $new = "../XML/link.xsl"; open(FILE, $filename) or die "Can't open `$filename': $!"; while (<FILE>) { if($_ =~ /HRWeb/){ #print "$."; $row = $.; $a = $row-1; $b = $row+2; print "HRWeb delete rows $a through "; } } &delete ($a, $b); sub delete{ open( OLD, "< $filename" ); open( NEW, "> $new" ); while ( <OLD> ) { next if /$a/../$b/; # copy everything but $a through $b print NEW $_; }print "$a deleted $b"; close( OLD ); close( NEW ); rename( $filename, "$filename.orig" ); rename($new, $filename ); }

    • ベストアンサー
    • Perl
  • テキストファイルの行抽出

    linux環境のプログラムについて質問です。 ある特定のファイル(テキストファイル)内のデータで 指定の行を抽出する方法を教えていただきたいと思います。 現在はawkを使用してbashスクリプト内で下記のように head, tailを使用していますが、処理が重いように 感じます。perlまたはawkなどで行抽出の軽い処理は できないでしょうか? (他のunixコマンドでも結構です。) ---------------------------------------- RNUM=`awk 'END{print NR}' $1` #行番号取得 for iwl in `seq 1 $RNUM`;do BASE=`head -n $iwl $1 | tail -n 1` done #iwl ---------------------------------------- Fortran, Cなども使えますが、色々組み合わせて使う上で bash内のスクリプトで行ないたいと思います。

  • perlでの文字抽出

    はじめて質問させていただきます。 perlでの文字抽出について教えてください! ---------------------------- #c,aaaaa,0000 #c,bbbbb,00000 #c,bbbbb,0000 : #s,dddd,AAAA,xxxxx,yyyyy time,1 id, 1 type,1 : #e #s,eeee,AAAA,bbbb,cccccc time,2 id,2 type,2 : #e #s,fff,BBBB,zzzzzz,kkkkk time,1 id,1 type,1 : #e #s,eeeee,AAAA,rrr,qqqqqqqqq time,3 id,1 type,3 : #e : ---------------------------- のようなテキストファイルがあります。 #sから#eまでがひとくくりで、それが繰り返し出現し、それぞれの#sから#eまでの長さはバラバラです。 このとき、#sの行の3番目の要素がAAAAで、かつid,1となっているものについて、それぞれの要素を抽出しcsvファイルに ---------------------------- time   id    type 1     1     1 3     1     3 : ---------------------------- のように書き出したいのですが、perlのプログラムはどのようにしたらよいのでしょうか? ---------------------------- while(<INFILE>){  if(/^#s/../^#e/){   if(($_=~/AAAA/){    if($_=~/id,1/)){ : ---------------------------- とするとif(($_=~/AAAA/)で、#sの行しか抽出できないためid,1の検索が出来ず、行き詰ってしまいました。 perlを勉強し始めたばかりの超初心者で、質問が分かりにくいかと思いますが、よろしくお願いします。

  • perlで特定行から特定行までを抜き出しor置換

    perlで以下のようなことをしたいと考えています。 以下のことを、コマンドラインから「perl ~」という形で 実行したいのですが、どのよにすればよいでしょうか。 1.特定行から特定行までを抜き出し 2.特定行から特定行の範囲で文字列置換 例えば、次のようなテキストファイルがあったとします。 example.txt ================================== #START aaaa hogehoge test okok perl script #END ================================== 上記ファイルを読み込んで、 1.「#START」から「#END」の間に挟まれた行だけ抜き出し 2.「#START」から「#END」の間で、「test」を置換 のです。 sedで言うところの sed -e '/#START/,/#END/ s/YYYYMM/201603/g' test.txt をやりたいのです。

    • ベストアンサー
    • Perl
  • perlを使用しての特定行抽出について

    何万行も書いてあるテキストファイルがあります。抽出したい文字列の一覧がテキストファイルにまとめられています(文字列は約1000個)。抽出したい文字列は一行にひとつづつ書かれています。何万行も書いてあるテキストファイルから抽出したい文字列と二つ合致したらその行を抽出したいです。ここで、注意していただきたいのが、抽出したい文字列一覧から二つの文字列にヒットした行を抽出したいです。宜しくお願い致します。 何万行も書いてあるテキストファイル aaaaa abc edi bb aert kkkkkkkkkk lllllll ddddd aaaaa anhi kkk    ・・・・・・・・ ・・・・・・・・ 抽出したい文字列一覧のテキストファイル aaaaa bbbbb ddddd iiiiiiiii eeeee ・・・・・ ・・・・・

    • ベストアンサー
    • Perl

専門家に質問してみよう