親子におすすめの新型プラネタリウムとは?

こんにちは。

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バイト文字の扱い方を御存じの方に、何かアドバイスを頂きたいと考えています。

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

このQ&Aに関連する最新のQ&A

A 回答 (5件)

> \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を利用しているのですか?



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

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

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

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

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

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

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

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

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



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

うまく行かなかったのは次の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

考えるのが面倒なので, 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

このQ&Aに関連する人気のQ&A

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

このQ&Aを見た人はこんなQ&Aも見ています

このQ&Aを見た人が検索しているワード

このQ&Aと関連する良く見られている質問

Qsedで日本語の置換方法について

sedを使い文字の置換を行いたいのですが、日本語(全角文字)の置換がうまくいきません。
たとえば[a]から[i]に変換することはできるのですが、[あ]から[い]にはうまく変換できません。
どなたかわかる方、変換する方法はありますでしょうか?あればその方法を教えてください。よろしくお願いします。

Aベストアンサー

#2です。
>回答してくださってありがとうございます。
>このコマンドを実行しても日本語の変換は行えませんでした。
こちらの環境では、変換できました。
D:\downloads\tools\sed_118d>type my.txt
ありがとうございます
abcdefg
おおああ漢字
いいいい
-----------以上は変更前のテキスト----
D:\downloads\tools\sed_118d>sed --ctype=sjis s/あ/い/g my.txt
いりがとうございます
abcdefg
おおいい漢字
いいいい
-------------上記が実行結果-------
確認ですが、
1.#2の参考URLのSEDを使用しましたか?
2.実際に打ち込んだコマンドは、どのようなコマンドですか。
3.変更前のテキストの漢字コードはシフトJISですね。
この漢字コードが、異なると変換出来るはずがありませんので、念のため為。
4.変更前のテキストが、もし、提示できるなら提示して下さい。
以上、補足して下さい。

#2です。
>回答してくださってありがとうございます。
>このコマンドを実行しても日本語の変換は行えませんでした。
こちらの環境では、変換できました。
D:\downloads\tools\sed_118d>type my.txt
ありがとうございます
abcdefg
おおああ漢字
いいいい
-----------以上は変更前のテキスト----
D:\downloads\tools\sed_118d>sed --ctype=sjis s/あ/い/g my.txt
いりがとうございます
abcdefg
おおいい漢字
いいいい
-------------上記が実行結果-------
確認ですが、
1.#2の参考URLのSEDを...続きを読む

Qsedコマンドで全ての2バイト文字を指定するには?

こんにちは。

LinuxやWindows用のsedコマンドには、[・・・]という演算子によって、
特定の範囲の文字集合が指定できます。
例えば
sed "s/[a-zA-Z]//g" test.txt
というコマンドによって、test.txtから、アルファベットを削除する事ができます。

では、シフトJISコードにおいて、全ての2バイト文字を指定するにはどうすればよいのでしょうか?
シフトJISコードでは、最初の漢字が「亜」であり、最後の漢字が「黑」らしいので、
sed "s/[あ-んア-ン亜-黑]//g"
といったコマンドを試しましたが、これでは
「ー」「、」「。」「ぁぃぅぇぉ」といった文字が残ってしまいます。

結局は、シフトJISコードの2バイト文字の範囲を知りたいのですが、
調べてみても分かりませんでした。

ご存知の方がいらっしゃれば、情報提供をお願い致します。

Aベストアンサー

そのsedがShift-JISの「1文字」を「1文字」として処理できるものである必要があります。
「マルチバイト文字に対応」と明記していないものでは、
[あ-んア-ン亜-黑]

「あ」の1バイト目
「あ」の2バイト目から「ん」の1バイト目
「ん」の1バイト目
...
とばらばらに解釈されている可能性があります。

あとは、Shift-JISのコード表を見れば、「あ-ん」には「ぁ」は入ってないし、句読点は別のところにあるのがわかると思います。
http://charset.7jp.net/sjis.html

> [・・・]という演算子
演算子ではないですが...

Qsedの置換文字に変数を使用したいのですが・・・

あるファイルの特定の文字を変換し、上書きをする処理を行いたいのですが、sedの置換文字に変数が渡せなくて困っています。

例:
X="a"
Y="b"
echo test.txt | sed 's/${X}/${Y/g}' >test.txt

sedでは置換文字に${X}といった変数を使用することはできないのでしょうか?

Aベストアンサー

' ・・・' で囲まれた中の$はそのままドルマークです。変数展開をするなら、'・・・'で囲んではいけません。

何も囲まないか、"・・・"で囲むかです。

Qgrepで全角文字の行を抽出

grepの検索でテキスト内のひとつでも全角文字がある行を
抽出したいと思ってます。1つの方法は見つかったのですが、
時たまバグがあるそうですので、
他の方法を探そうと思ってます。
わかっている方法は、以下のとおりです。
> grep -n '.*[^ -~。-゜].*' test_data.txt

よろしくお願いします。

Aベストアンサー

「ASCIIに含まれる文字だけ」の行以外という案で、
LANG=C で、ロケール(言語)を英語にした上で、grep -v で除外を指定。

LANG=C grep -n -v '^[[:cntrl:][:print:]]*$' test_data.txt

Qテキストファイル内容の、16進数コードでの検索方法は?

EUC-JPなテキストファイルをその場の環境に応じてLinux、Windowsの両方の環境で書いていると時々行末に余分な制御文字0dH(16進数コード)が入ったりします。
そんな時は、
$ sed -e "s/\x0d$//" INPUT_FILE > OUTPUT_FILE
としています。

これに関連して、Linux上で、エディタを開かずにテキストファイル内の 0dH の有無を確かめるにはどのようなコマンドが適切でしょうか。(いちいち調べずに、sedに流し込めばよいのですが)

なお、下記では、検索できませんでした。
$ grep "\x0d" INPUTFILE

2006年2月11日
RASUM2

Aベストアンサー

grep には引数を十六進指定する機能は無いので、シェルの機能に頼ることになります。
bashなら、$'…' という記法が使えるので(詳細はman bash の $' の説明参照)、
grep $'\x0d' INPUTFILE と書けます。

Qsedなどで、特定の文字列の後の文字列を抽出したい

sedなどで、特定の文字列の後の文字列を抽出したい

シェルスクリプト内で、sedなどを使って特定の文字列の後の文字列を抽出したいのですが、どうすればいいでしょうか?

たとえば、abcXYZ123defghiのなかから、XYZの後の「123」を抜き出したいです。

echo abcXYZ123defghi | sed ...

のようにして実行させたいです。

Aベストアンサー

日本語対応sedだと日本語数字混じりでもできますね。

echo abcXYZ12357defghi | sed -e 's/^.*XYZ\([0-90-9]*\).*$/\1/' -e 'y/0123456789/0123456789/'
12357

※ 使っている日本語コードの指定は必要かも(例えば、 --ctype=EUC)

echo abcXYZ12357defghi | sed -e 's/^.*XYZ\([0-90-9]*\).*$/\1/'
12357

※ 入力フォームに書いている時はASCIIと日本語の判別がし易いけど回答見るとわかり難いですね。後ろの例での結果57とy/0123456789/の数字部分が日本語です。


人気Q&Aランキング