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

こんにちは。

sedコマンドは、texに付属していた日本語対応のものを使用しており、
perlはWindows用のActive Perl v.5.12.2を使用しています。

カレントディレクトリにあるファイルやフォルダにおいて、
名前を一括して変換したいと思い、以下のようなNameConv.batというバッチスクリプトを作成しました。
____________________________________________________________
@echo off
for /f "delims=" %%a in ('dir /b !opt!') do (
for /f "usebackq delims=" %%b in (`echo %%a^| sed -e "s/%~1/%~2/g"`) do (
move /y "%%a" "%%b" 1>nul 2>nul
if not "%%a"=="%%b" echo 「%%a」→「%%b」
)
)
____________________________________________________________

このバッチスクリプトを
NameConv.bat " " "_"
の様にして呼び出すと上手く行きました。
(この場合は、カレントディレクトリの全てのディレクトリとフォルダの名前において、
スペースがアンダーバーに変換されました。)

ところが、
NameConv.bat "スペース" " "
のように、第一引数に変換文字列に2バイト文字を使用すると、
以下のようなエラーメッセージが出て上手く行きませんでした。

sed: -e expression #1, char 14: Unterminated `s' command

どうやら、
echo %%a| sed -e "s/スペース/ /g"
といったコードは、上手く行かなかったようです。

そこで、この問題を解決しようと、以下のようなNameRep.plというPerlスクリプトを作成しました。
____________________________________________________________
#日本語を扱うために必要な設定
use encoding "cp932";
use open ":encoding(cp932)";
use open ":std";
map{ binmode($_,":crlf"); } qw/STDIN STDOUT STDERR/;

#置換操作
$name="$ARGV[0]";
$name =~ s/$ARGV[1]/$ARGV[2]/g;
print "$name";
____________________________________________________________

そしてこのスクリプトを
for /f "delims=" %a in ('dir /b') do ( @perl NameRep.pl "%a" "スペース" "_" )
の様にしてコマンドラインから呼び出すことで、「スペース」という文字列が、
きちんと処理できるかを試したところ、以下のようなエラーメッセージが出ました。

Unmatched [ in regex; marked by <-- HERE in m/\x{00d8}\x{00f9}\x{005b} <-- HERE
\x{00d8}/ at C:\Users\kei\NameRep.pl line 11.

どうやら
$name =~ s/$ARGV[1]/$ARGV[2]/g;
の部分で、$nameや$ARGV[1]に2バイト文字が含まれていると、上手く行かないようです。

#日本語を扱うために必要な設定
以下のコードブロックで、シフトJISに対応できるはず(少なくとも今までは問題なく扱えました)
なのですが、何故か上手く行きません。

そこで、sedコマンドやperl、特にperlにおいて、シフトJISコードでの2バイト文字の扱い方を御存じの方に、何かアドバイスを頂きたいと考えています。

長くなりましたが、どうぞよろしくお願い致します。

A 回答 (5件)

考えるのが面倒なので, Encode::decode とか Encode::encode とかを駆使したらどうだろうか.

この回答への補足

ちなみに、texのsedコマンドの代わりに、onigsedというコマンドをダウンロードして使うことで、今回の問題は解決できました。
ただ、perlでの2バイト文字の扱いについて、もっと詳しく知りたいので、
引き続き、よろしくお願い致します。

補足日時:2011/08/19 03:33
    • good
    • 0
この回答へのお礼

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

encode関数は使った事がありますので、少しは知っています。
例えば、2バイト文字を含む名前を持つファイルを扱う場合は、
以下のような関数を、perlスクリプトの先頭にでも記述しておけば、
このopen関数で、2バイト文字を含む名前を持つファイルをファイルを開く事ができます。
____________________________________________________________
use subs qw(open);
sub open {
use Encode;
return CORE::open(shift,encode('cp932',shift),encode('cp932',shift));
}
____________________________________________________________

今回のNameRep.plでは、どのようにしてencode関数やdecode関数を使えばよいのでしょうか?
perlについて初心者なので、具体的なコードを記述して、
説明して下さると大変助かります。

お礼日時:2011/08/19 03:30

うまく行かなかったのは次の2点が原因と思われます。



(1)use encodingプラグマは@ARGVに対して効力を発揮しない

@ARGVにはcp932文字列がバイナリとして入ってきます。
従って、#置換操作 の前に次の一文を置いてやれば動きます。

$_ = Encode::decode('cp932', $_) foreach (@ARGV);


(2)置換の検索キーワードである$ARGV[1]は正規表現として扱われる

$ARGV[1]に正規表現の制御文字が含まれている場合、意図する置換にはなりません。
今回の場合、(1)が原因でcp932文字列がバイナリとして扱われた結果、
cp932文字の2バイト目がたまたま '[' だったために不正な正規表現としてエラーになったと
思われますが、実際$ARGV[1]にこれらの記号を入れてやると同じエラーが起きます。
従って、\Q~\Eなどで適切にエスケープしてやる必要があります。

$name =~ s/\Q$ARGV[1]\E/$ARGV[2]/g;


以上でおそらくご質問の回答にはなっていると思いますが、老婆心ながら忠告を。

use encodingプラグマは今回のご質問のようなケース以外にも副作用が非常に
多かったため、現在は「使用が推奨されないプラグマ」とされています。
Encodeモジュールのメンテナである小飼弾さんが「黒歴史」と明言されているほどです。
http://blog.livedoor.jp/dankogai/archives/512217 …
基本的にはかつて存在したJperl用の古いソースコードを無理やり動かす専用の機能と割り切るべきと考えています。

せっかくここまで書ける知識をお持ちなのですから、新規で書くスクリプトについては
現在推奨されている記述方法:
・PerlのソースコードはUTF-8で保存する
・ソースコードにマルチバイト文字を含む場合は use utf8プラグマを使う
・use strict; use warnings; も忘れずに
を強く推奨します。

ソースコードをWindowsネイティブであるcp932で書けるというのはなかなか捨てがたい魅力では
あるのですが、それを捨てるだけの価値があるケースがきっと今後出てくると思います。
# use encodingしてるとうまく動かないCPANモジュールがいっぱいあるんですよね・・・
    • good
    • 0
この回答へのお礼

大変御丁寧な御回答、ありがとうございます。

提供された御説明で、幾つか分からなかった所があるので、質問させて下さい。

・仰られたように
$_ = Encode::decode('cp932', $_) foreach (@ARGV);
を記述すれば、
$name =~ s/\Q$ARGV[1]\E/$ARGV[2]/g;
とせずに
$name =~ s/$ARGV[1]/$ARGV[2]/g;
のままでも上手く行きました。
\Q$ARGV[1]\E は、$ARGV[1]に正規表現が含まれていても、
普通の文字列として扱うという事なのでしょうか?

・「cp932文字列がバイナリとして入ってきます。」というのは、
2バイト文字は、2つの1バイト文字の並びとして入ってくる、つまり、
内部では2バイト文字として扱われないという意味でしょうか?

・最後に、
use strict; use warnings;
は、いままでソースファイルに記述してこなかったのですが、
これらはなぜ記述するべきなのでしょうか?

お礼日時:2011/08/21 07:02

> \Q$ARGV[1]\E は、$ARGV[1]に正規表現が含まれていても、


> 普通の文字列として扱うという事なのでしょうか?

はい、その通りです。私がご質問の内容を見落としていたのですが、
スクリプトの引数に正規表現を意図的に使えるようにしたいということであれば
(=sedと同様の動作を期待するなら)\Q \Eは全く必要ありません。失礼しました。
サンプルスクリプトのエラーの原因を説明するために必要だったということでご容赦ください。
もちろんその際は、各種カッコやピリオド等を必要に応じて手動エスケープしてやる必要があります。
(例:「a.txt」が「abtxt」にマッチしてしまうのを防ぐ→「a\.txt」にする)

> ・「cp932文字列がバイナリとして入ってきます。」というのは、
> 2バイト文字は、2つの1バイト文字の並びとして入ってくる、つまり、
> 内部では2バイト文字として扱われないという意味でしょうか?

はい、そうです。length()で文字数を確認してみるとわかりやすいです。
マルチバイト文字を1文字として扱うには、Encodeモジュール等でテキストを内部utf8表現にしてやる必要があります。
バイナリはlength()がバイト数を返すのに対して、内部utf8表現は文字数を返します。
これはUTF-8文字も同様です。UTF-8であってもデフォルトではバイナリとして扱われます。
つまり、一般的なUTF-8と内部utf8表現は異なるものです。
少々ややこしい話になるので、詳細は「Perl utf8」等で検索してみてください。

> ・最後に、
> use strict; use warnings;
> は、いままでソースファイルに記述してこなかったのですが、
> これらはなぜ記述するべきなのでしょうか?

スクリプトの潜在的なバグを回避するのに極めて有効な手段だからです。
今回のような小規模なスクリプトでは恩恵は少ないように思われるかもしれませんが、
ミスを防ぐために冒頭につけておくのはよい習慣とされています。
大雑把に説明すると、

・use strictプラグマ - 宣言のない変数の使用等をエラーにする

use strict;
# my $hoge = 'abc'; # 変数宣言をコメントアウトすると・・・
print $hoge; # 宣言されていない変数$hogeが使われたよ、とエラーになる

・use warningsプラグマ - 未定義の値に対する操作や同名変数の二重定義等に対して警告を出す

use warnings;
my $hoge = 'abc'; # 変数宣言
$hoge = 'def'; # 変数の使用
my $hoge = 'ghi'; # 同名変数が同じスコープで宣言されたよ、と警告が出る

といったところです。詳細は検索してみてください。
    • good
    • 0
この回答へのお礼

大変御丁寧な御説明、ありがとうございます。
御説明頂いた内容で、分からなかった事は理解できました。
Perlというのは便利ですが、2バイト文字を扱うとなると、結構厄介な問題が多いのですね。
Perlについては色々と勉強してみたのですが、現在は文字列の置換にしか応用できていないので、これからもっと応用が利くように頑張りたいと思います。
ちなみに、N60-BASICさんは、どんな場面でPerlを利用しているのですか?
大変優秀な方なので、他にどんなプログラミング言語を使うのなんかも教えていただきたいと思いました。
僕は普段、C言語、バッチスクリプト、CygwinでのShellといったものを使ってプログラミングを行っています。

お礼日時:2011/08/22 11:33

一応突っ込んでおくと, Perl に限らずどんなプログラム言語であっても 2バイト (以上) の大きさを持つ文字を扱おうとすると面倒です. 厳密には「1バイト文字だけを扱っていても面倒」なんだけど.



そして Unicode にもやっぱり暗黒面があったりする.
    • good
    • 0

> どんな場面でPerlを利用しているのですか?



Webプログラミング、サーバー運用(解析、監視、etc.)、複数システム間のデータ連携などの業務にPerlやJavaScriptを使っています。

Perlを使ってる理由ですが、

・旧バージョンとの後方互換性がある程度担保されるため、比較的長期運用が必要な社内システム等の構築が容易。プラットフォームOSへの依存が少なく、機器更新時の選択自由度が高い
・強力かつ大量のCPANモジュールが利用可能
・コンパイル不要。迅速な修正が可能。最悪担当者が逃げてもソースコードがあればなんとかなるよね・・・?(^^;

最後のは半分冗談ですが、こんな感じです。
Perlもまだまだ勉強中なので、しっかり身につけてから他の言語に手を出そうかなと思ってます。
Python、Rubyあたりには興味がありますね。用途次第でしょうけど、ご参考まで。
    • good
    • 0
この回答へのお礼

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

なるほど。
結構複雑な事にも、Perlは応用できるのですね。
Perlを使っておられる理由も、参考になりました。

色々とありがとうございました。

お礼日時:2011/08/25 14:31

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