アプリ版:「スタンプのみでお礼する」機能のリリースについて

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コマンドでは不便なのです。

上記の問題の原因が分かる方がおられましたら、お教え頂けないでしょうか。

A 回答 (6件)

〔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/);
    • good
    • 0
この回答へのお礼

ご回答、ありがとうございます。大変よく分かりました。

実際に作業で使うリスト内の単語は様々で、「あaいi」の様に、2バイト文字と半角文字が複数回交互に来る場合もあるので、単語リストをスクリプトに置換する時、「print if (/あ(?i)aいi/);」にすると、2バイト文字の「い」が「(?i)」より後に来てしまいますし、「print if (/あaい(?i)i/);」とすれば「a」が半角のみにマッチしてしまうと思うので、なかなか難しいです。

実際作業に使用するリストの単語は、2文字以上の場合が多いので、今回の件が原因での不具合は起こり辛いのですが、不完全な仕組みで長期間処理を続けて、いつか不具合が起きると嫌なので、出来れば完璧に間違いのない行の抽出をしたいのです。

ただ、ご回答の内容で大変よく分かりましたので、なんとか解決策を見付けられそうです。ご回答、ありがとうございました。

お礼日時:2014/08/26 16:53

入力 (と出力) の際に, 内部コードとの間で文字コードを変換する.



必要であればスクリプトで使っている文字コードも指定する.

この回答への補足

色々調べてみたのですが、単純にスクリプトに1、2行足せばいいというものではなく、もう少し複雑になる様で、そうなると色々と勉強しなければつまずく箇所が多いので、今回はこちらの方法は見送ろうと思います。

一応、WEBにあったサンプルのコードを、そのままコピペして実行してみたのですが、環境が合っていないのか、エラーが出てうまく行かないので、実現まで時間が掛かりそうなのです。

Perlに関する本は手元にあるので、時間がある時に勉強して、マスターしたら今回の問題を解決しようと思います。

ご回答、ありがとうございました。

補足日時:2014/08/26 22:05
    • good
    • 0
この回答へのお礼

ご回答、ありがとうございます。

Perlのスクリプト内に、ファイルからのデータを入出力の際、文字コードを変換する処理を書くということでしょうか。

自分はあまりPerlに詳しくないので、今から調べてやってみようと思います。

出来たらまた追記したいと思います。ご回答ありがとうございました。

お礼日時:2014/08/26 17:22

書くのが面倒と言えば面倒なのですが、(?i) には別の書き方があります。



print if (/あ(?i:a)い(?i:i)/);

?i の直後にコロンを置いて、続けてパターンを書けば外側に影響することはありません。ただ、Shift_JIS の2バイト目には、バックスラッシュや正規表現のメタ文字も含まれるので注意が必要です。
    • good
    • 0
この回答へのお礼

再度、ご回答ありがとうございます。

正規表現で置換コマンドの一文を追加するだけなので、面倒という訳ではないのですが、思い通りの動作をしてくれるかが心配です。

今回お教え頂いた方法を使う場合、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]/);」なら
どちらがいいのかという事です。同じでしょうか。

とにかく、もう少し調べてみようと思います。ご回答ありがとうございました。

お礼日時:2014/08/26 21:55

現状どんなスクリプトなのかまったくわからんので想像するしかないんだけど, たいてい


ファイルから入力する (このときに内部コードに変換する)→なんかする→出力する (このときに内部コードから変換する)
でいけると思うんだ.

あ, もちろん Perl のバージョンに依存する部分はあるけどね.
    • good
    • 0
この回答へのお礼

再度、ご回答ありがとうございます。

私は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.

--------------------------------

これで目的は達成出来ましたので、後は地道に調べてエラーも出来れば消そうと思います。

あと何時間かしたら質問を締め切らせて頂こうと思いますので、それまでにもし何かアドバイス等ございましたら頂けると有り難いです。

ご回答ありがとうございました。

お礼日時:2014/08/27 11:55

すみません, ちょっと確認ですが


print if ( /〔a/i );

print Encode::encode('utf-8', $_) if ( /〔a/i );
にしたら (これでエラーになるなら use Encode; を最初の方に追加して上の Encode::encode をただの encode にする) どうなるでしょうか?

余談ですが, この位置に if をつけるときにはかっこはなくてもよかったりします.

この回答への補足

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

補足日時:2014/08/27 16:15
    • good
    • 0
この回答へのお礼

「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にあったサンプルコードを自分なりに書き換えて使っていまして、使い方を間違っている部分もありますね。お恥ずかしいです。

おかげさまで完璧に行の抽出が出来るようになりました。大変助かりました。ご回答、本当にありがとうございました。

お礼日時:2014/08/27 15:52

> 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」もそのままで実行できると思います。)
    • good
    • 0
この回答へのお礼

再度、ご回答ありがとうございます。

なるほど、つまり、「[aA]」の様にしても意味がないという事ですね。Shift_JIS使うと大変ややこしくなるのですね。大変参考になりました。今後はなるべくUTF-8等を使って行こうと思います。

「〔」と「゜」の問題は、Perlスクリプト内で文字コードをUTF-8にする事で解決出来ました。ありがとうございました。

今回は色々と勉強になりました。長い間付き合って頂き、本当にありがとうございました。大変助かりました。

ご回答、本当にありがとうございました。

お礼日時:2014/08/27 16:12

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!