0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Perlのbinmodeでハマる

Last updated at Posted at 2024-06-01

はじめに

WindowsでSJISデータをperlで処理するときに、ハマりました。
コマンドプロンプトにSJISデータを標準出力表示させたら、文字化けするのです。
同じワナにハマる人がいるかもしれないので、備忘録として書いてみます。

ポイントだけ

・binmodeのレイヤはスタックされるので注意。
・複数ファイルに分かれるperlスクリプトではbinmodeでencoding変えるのは注意。
・スタック回避にはrawが使えるが、出力データ毎にencodeした方が安全。

何が問題だったか

「コマンドプロンプトに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でのencoding指定が必要な場合にはどうしたら良いのでしょうか?
この場合、: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.

「STDOUTなど共通出入口にbinmode ncoding」はまとめて設定できるので楽ですが、複数ファイルに分かれるperlスクリプトでは注意が必要です。面倒だけど外に出すデータ毎にencodeした方が失敗少なそうです。

0
0
1

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?