LoginSignup
1
1

Perlのbinmodeでハマる

Last updated at Posted at 2023-08-21

はじめに

WindowsでSJISデータをperlで処理するときに、ハマりました。
コマンドプロンプトにSJISデータを標準出力表示させたら、文字化けするのです。
同じワナにハマる人がいるかもしれないので、備忘録として書いてみます。
私はperlエキスパートではないので、間違いとかもっと良い手段あるとか、あったら教えてください。

何が問題だったか

「コマンドプロンプトにSJISデータを標準出力表示」させるために以下を入れました。

binmode STDOUT, ':encoding(cp932)';

スクリプトはUTF-8で書いています。

ここで、「binmode」を複数個所で入れていました。
ところが、binmodeのレイヤはスタックされるので、2重変換になって文字化けします。

ダメだったスクリプトを単純化したものが、以下です。これ、文字化けします。

use strict;
use utf8;

binmode STDOUT, ':encoding(cp932)';
binmode STDOUT, ':encoding(cp932)';

my $word = "ソート機能\n";
print $word;

こんな風に1つのファイルに2個書いていたらすぐ気づくのですが、スクリプトを分割してモジュール化し、そのモジュールにもbinmodeを入れていました。
スクリプトもモジュールもステップ大きいと、すぐに気づきません。

use strict;
use utf8;
use MyModule;

binmode STDOUT, ':encoding(cp932)';

MyModule::MyFunc();   #  この中にbinmodeあり

my $word = "ソート機能\n";
print $word;

じゃぁ、どうしたらいいの?

私はモジュール側のbinmodeを外しました。
モジュールの中でSTDOUTに出力していたのはデバッグ用途のためで、必須ではなかったからです。

でも、本当にモジュール側でもbinmodeが必要な場合にはどうしたら良いのでしょうか?
この場合、:rawレイヤを付けると既存の変換を無効化してくれそうです。

例えば、本体スクリプトもモジュール側も以下で書くと正しく動作します。

binmode STDOUT, ':raw:encoding(cp932)';

ただ、このレイヤの話、いろいろ機能が混じっていて難しいです。

一応、Perl自身の説明読むと

The implementation of :raw is as a pseudo-layer which when "pushed"
pops itself and then any layers which would modify the binary data stream.

と書いてあるので、過去に積んだ変換のレイヤーはチャラにしてバイナリにするようですが、この「過去に積んだ変換のレイヤーはチャラ」のためだけに:rawを使ってよいものか、まだ見切れていないのです。

さいごに

WindowsのSJISデータ処理にperlを使うのが、そもそも間違いだったかもしれません。過去の歴史もあるのでしょうが、perlでの文字コード操作は泣きたくなるくらい、難解です。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1