
こんにちは。
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バイト文字の扱い方を御存じの方に、何かアドバイスを頂きたいと考えています。
長くなりましたが、どうぞよろしくお願い致します。
No.3ベストアンサー
- 回答日時:
> \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'; # 同名変数が同じスコープで宣言されたよ、と警告が出る
といったところです。詳細は検索してみてください。
大変御丁寧な御説明、ありがとうございます。
御説明頂いた内容で、分からなかった事は理解できました。
Perlというのは便利ですが、2バイト文字を扱うとなると、結構厄介な問題が多いのですね。
Perlについては色々と勉強してみたのですが、現在は文字列の置換にしか応用できていないので、これからもっと応用が利くように頑張りたいと思います。
ちなみに、N60-BASICさんは、どんな場面でPerlを利用しているのですか?
大変優秀な方なので、他にどんなプログラミング言語を使うのなんかも教えていただきたいと思いました。
僕は普段、C言語、バッチスクリプト、CygwinでのShellといったものを使ってプログラミングを行っています。
No.5
- 回答日時:
> どんな場面でPerlを利用しているのですか?
Webプログラミング、サーバー運用(解析、監視、etc.)、複数システム間のデータ連携などの業務にPerlやJavaScriptを使っています。
Perlを使ってる理由ですが、
・旧バージョンとの後方互換性がある程度担保されるため、比較的長期運用が必要な社内システム等の構築が容易。プラットフォームOSへの依存が少なく、機器更新時の選択自由度が高い
・強力かつ大量のCPANモジュールが利用可能
・コンパイル不要。迅速な修正が可能。最悪担当者が逃げてもソースコードがあればなんとかなるよね・・・?(^^;
最後のは半分冗談ですが、こんな感じです。
Perlもまだまだ勉強中なので、しっかり身につけてから他の言語に手を出そうかなと思ってます。
Python、Rubyあたりには興味がありますね。用途次第でしょうけど、ご参考まで。
御回答ありがとうございます。
なるほど。
結構複雑な事にも、Perlは応用できるのですね。
Perlを使っておられる理由も、参考になりました。
色々とありがとうございました。
No.4
- 回答日時:
一応突っ込んでおくと, Perl に限らずどんなプログラム言語であっても 2バイト (以上) の大きさを持つ文字を扱おうとすると面倒です. 厳密には「1バイト文字だけを扱っていても面倒」なんだけど.
そして Unicode にもやっぱり暗黒面があったりする.
No.2
- 回答日時:
うまく行かなかったのは次の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モジュールがいっぱいあるんですよね・・・
大変御丁寧な御回答、ありがとうございます。
提供された御説明で、幾つか分からなかった所があるので、質問させて下さい。
・仰られたように
$_ = 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;
は、いままでソースファイルに記述してこなかったのですが、
これらはなぜ記述するべきなのでしょうか?
No.1
- 回答日時:
考えるのが面倒なので, Encode::decode とか Encode::encode とかを駆使したらどうだろうか.
この回答への補足
ちなみに、texのsedコマンドの代わりに、onigsedというコマンドをダウンロードして使うことで、今回の問題は解決できました。
ただ、perlでの2バイト文字の扱いについて、もっと詳しく知りたいので、
引き続き、よろしくお願い致します。
御回答ありがとうございます。
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について初心者なので、具体的なコードを記述して、
説明して下さると大変助かります。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) ファイル名の右側を変更したい ファイル名:「1001日別売上」の左側へ「2022」を追加し、「202 6 2022/10/14 10:03
- Visual Basic(VBA) 入力ボックスが繰り返しポップアップして止まらない。 下記コードでファイル名の変更をしたいのですが、変 1 2022/09/08 11:27
- Visual Basic(VBA) 集めたシートのシート名を変更したい。 下記のコードでサブフォルダにあるファイルのSheet3を集めて 6 2022/08/23 10:38
- JavaScript Javascriptが機能せず原因が分からないので教えて頂きたいです 3 2023/06/04 14:50
- その他(プログラミング・Web制作) バッチファイルでPCのモデル名を取得したい 1 2022/03/31 10:58
- Visual Basic(VBA) 特定の文字を簡単な操作で半角スペースに変換するか削除したい 2 2022/11/01 10:35
- UNIX・Linux Linuxについて質問です。 以下のhistoryの出力結果から、sedコマンドのファイル名tmp1 1 2023/02/03 20:11
- Perl perlをバージョンアップしたら、今まで正常に動いていたプログラムが、エラーになってしまった 3 2022/10/05 15:44
- Visual Basic(VBA) あるフォルダーのファイルを違う親フォルダーのサブフォルダーに移したい 11 2023/02/15 19:00
- その他(プログラミング・Web制作) sedの動作 2 2022/10/10 13:38
このQ&Aを見た人はこんなQ&Aも見ています
関連するカテゴリからQ&Aを探す
おすすめ情報
このQ&Aを見た人がよく見るQ&A
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ソース中の文字列と、DBから取...
-
文字コードについて \\x{53d6}...
-
Jcodeによるカタカナ→ひらがな変換
-
perl でエラー:Wide character...
-
CSVファイルの中で、「 , 」カ...
-
住宅にカナを入力する際に丁目...
-
VBA 文字に半角が含まれて...
-
Perlで一行で文字列の置き換え
-
英数字のみ全角から半角に変換
-
エクセルで数値を全角文字(カ...
-
cgi perlの条件式にて
-
全角英数字の必要性が理解できない
-
位置指定の文字マッチングと同...
-
1つのセルに1つ以上のデータ...
-
Excelの中に全角ひらがな、漢字...
-
このHPのアドレスバーの区切り...
-
WORDで改ページすると時々グレ...
-
CString から LPCTSTRの型に変換
-
Accessのテキストボックスの入...
-
文字列の一致について
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
perl でエラー:Wide character...
-
MFCのコントロールにUTF-8の文...
-
【文字コード】外見上は全く同...
-
printfの書式指定での2バイト文...
-
【LaTeX】pBibTeXでのエラーの...
-
デバッグ情報が文字化けする
-
perlについての質問
-
漢字コードについて
-
カタカナ一覧表が知りたい
-
Jcode.pmでconvertできない
-
Jcodeによるカタカナ→ひらがな変換
-
オブジェクト? perlをオーバー...
-
jcode.plのかわり
-
HTML::Templateでutf-8のテンプ...
-
Perlにおけるパターンマッチ
-
UTF-16からUTF-8への変換
-
UTF8コードでのメール送信
-
EC-CUBEでの文字化け
-
sedやperlでの2バイト文字を含...
-
Python データの可視化
おすすめ情報