Wide character in null operation at *.pl line n. の対策

ここでは、use utf8 プラグマが指定されているときに発生するエラー“Wide character in null operation at *.pl line n.”に対して、とりあえず回避できた方法を紹介しています。

use utf8 プラグマが指定されていない別のスクリプトへデータを渡すときにも、同じエラーが発生することがあります。
その場合も、同じ対処法で逃げられました。

テスト環境

Windows XP Professional Service Pack 3
ActivePerl 5.8.8 Build 822
DB_File-1.816
Encode-2.33

サンプル コード

以下に、問題が発生するコードのサンプルを示します。
処理の結果は、Windows ユーザに優しい shiftjis で出力されますが、スクリプトは utf8 で保存してください。

#!/usr/local/bin/perl

use utf8;
use strict;
use Encode;

use DB_File;
use Fcntl;

binmode STDOUT, ":encoding(shiftjis)";

# 変数の設定
my %hash_sample = ();
my $key = 'キー';
my $value = '値';

# ハッシュをデータベースに接続
tie %hash_sample, 'DB_File', "./hash_sample_db", O_RDWR | O_CREAT, 0600;

# ハッシュに適当な値を代入
$hash_sample{$key} = $value;

# データベースの内容を利用した処理
print $hash_sample{$key};

# データベースの利用を終了
untie %hash_sample;

上記のコードを実行した際に表示されるエラー メッセージは、以下のようになります。
Wide character in null operation at *.pl line 21.

対処法と対策済みサンプル コード

データが Perl の処理系から出ていく寸前に、UTF-8 フラグを落とします。
逆に、外から入ってきたデータには UTF-8 フラグを立ててあげます。

以下に、問題を修正したコードのサンプルを示します。
処理の結果は、shiftjis で出力されます。
スクリプトは utf8 で保存してください。

#!/usr/local/bin/perl

use utf8;
use strict;
use Encode;

use DB_File;
use Fcntl;

binmode STDOUT, ":encoding(shiftjis)";

my %hash_sample = ();

my $key = 'キー';
my $value = '値';

tie %hash_sample, 'DB_File', "./hash_sample_db", O_RDWR | O_CREAT, 0600;

# UTF-8 flag オフ
$key = encode("utf8", $key);
$value = encode("utf8", $value);

$hash_sample{$key} = $value;

# UTF-8 flag オン
print decode("utf8", $hash_sample{$key});

untie %hash_sample;

もちろん、実運用環境下ではデータベースのロックなどを忘れてはいけません。

余談

NIVOSIdE.jp などで稼働中のシステムでは、特に外部のウェブ サービスを利用する局面において、サービス提供者の負荷軽減とレスポンスの改善を目的としてデータを積極的にキャッシュし、一定期間内にクライアントから同じ要求があったときにはキャッシュされたデータを優先的に出力するようにしています。

そのキャッシュ機構の開発中に、データをデータベースに格納する時点で発生するエラーにとても苦しみました。
そして当座の解決策にたどり着いたときには……、正直これでよいのかと。

そのうち必要がなくなったり、もっと良い解決法が存在する可能性も考えました。
しかし、同じ苦しみをもつ開発者にとりあえずの対策を伝えたい、それからのことは各自で対処して欲しいと、そんな気持ちで公開しています。

うっかり本音が出ました。


Powered by NIVOSIdE

今後 Google Analytics によるアクセス解析に記録を残さない