■ サブルーチン名
EncodeSubject
■ 引数
- サブジェクト文字列
■ 戻値(配列)
- RFC2047に従いBASE64 Bエンコードされたサブジェクト
■ 使い方
以下のスクリプト例のように、メール送信をするCGIにおいて利用できます。このサブルーチンは、日本語文字コード変換ライブラリー「jcode.pl」が必要ですので、かならず、スクリプトで jcode.pl をインクルードしてください。
#!/usr/local/bin/perl
require "./jcode.pl";
#サブジェクトをスカラー変数 $String に格納
$Subject = "日本語サブジェクト";
#サブルーチン EncodeSubject を使って、
#サブジェクトをBase64 Bエンコード
$EncodeSubject = &EncodeSubject("$Subject");
#メールを送信
open(MAIL, "|/usr/lib/sendmail -t");
print MAIL "To: hoge@hoge.com\n";
print MAIL "From: foo\@bar\.com\n";
print MAIL "Subject: $EncodeSubject\n";
print MAIL "MIME-Version: 1.0\n";
print MAIL "\n";
print MAIL "test test test\n";
close(MAIL);
#結果をブラウザーに出力
print "Content-Type: text/html\n\n";
print "送信しました。\n";
■ 説明
引数として日本語サブジェクトの文字列を格納したスカラー変数を与えると、RFC2047に従い、JISコードに変換し、Base64 Bエンコードされた文字列を返します。例えば、「日本語サブジェクト」という文字列を与えると、
=?ISO-2022-JP?B?GyRCRnxLXDhsJTUlViU4JSclLyVIGyhC?= を返します。メール送信などのスクリプト上では、
print MAIL "Subject: $EncodeSubject\n"; のように、「Subject: 」と改行「\n」を付け加える必要がありますので、ご注意ください。
RFC2047では、エンコードされた状態で、全体の文字数が(「Subject: 」を含めて)76文字を超える場合には、改行する必要があります。このように長いサブジェクトにおいても、正しく改行した状態で値を返します。例えば、「長くて長くて長くて長くて長くて長くてとっても長〜い長〜い長〜い日本語サブジェクト」の場合には、
=?ISO-2022-JP?B?GyRCRDkkLyRGRDkkLyRGRDkkLyRGRDkkLyRGRDkbKEI=?= =?ISO-2022-JP?B?GyRCJC8kRkQ5JC8kRiRIJEMkRiRiRDkhQSQkRDkbKEI=?= =?ISO-2022-JP?B?GyRCIUEkJEQ5IUEkJEZ8S1w4bCU1JVYlOCUnJS8bKEI=?= =?ISO-2022-JP?B?GyRCJUgbKEI=?=を返します。2行目以降の最初には、半角スペースが入ります。
■ 制限事項
このサブルーチンは、RFC2047に完全には準拠していません。RFC2047では、ASCII文字列は、エンコードしないことを推奨していますが、このスクリプトでは、簡略化するために、すべてをエンコードしています。
例えば、「test 日本語サブジェクト」というサブジェクトの場合には、RFC2047では、
test =?ISO-2022-JP?B?GyRCRnxLXDhsJTUlViU4JSclLyVIGyhC?= となることを推奨しています。「test」の部分がエンコードされていませんね。それに対して、このサブルーチンでは、以下のように、「test」を含めて一括でエンコードしています。
=?ISO-2022-JP?B?ZXN0IBskQkZ8S1w4bCU1JVYlOCUnJS8lSBsoQg==?= RFC違反というわけではなく、ほとんどのメーラーでは問題なくデコードできますので、さほど大きな問題は発生しないかと思います。
■ サブルーチン
sub EncodeSubject {
my($String) = @_;
&jcode::convert(\$String, "euc");
my($Base64Table) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
'abcdefghijklmnopqrstuvwxyz'.
'0123456789+/';
my($chunk, $ByteChunk, $PackedByteChunk, $DecimalNum, $EncodedString);
my($SplitedWord, @SplitedWordList, $i, $Byte, $Buff);
my($KI) = 0;
my($KO) = 0;
my($CharNum) = 0;
my($CharType) = 0;
my($LineLength) = 0;
my($CharEndFlag) = 1;
if($String =~ /[^a-zA-Z0-9\!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\/\^\_\~ ]/) {
$i = 0;
@SplitedWordList = ();
while($i < length($String)) {
$Byte = substr($String, $i, 1);
if($Byte =~ /[\x8E\xA1-\xFE]/) {
unless($CharType eq 'K') {$KI ++;}
$CharType = 'K';
if($CharEndFlag) {
$CharEndFlag = 0;
} else {
$CharEndFlag = 1;
}
} else {
if($CharType eq 'K') {$KO ++;}
$CharType = 'A';
$CharEndFlag = 1;
}
$Buff .= $Byte;
$CharNum += 1;
$LineLength = 27 + ($CharNum*4/3) + (($KI+$KO)*4) + 2;
if($CharType eq 'K') {$LineLength += 4;}
if($CharEndFlag && $LineLength>=70) {
&jcode::convert(\$Buff, "jis");
push(@SplitedWordList, $Buff);
$Buff = '';
$CharNum = 0;
$CharType = 0;
$KI = 0;
$KO = 0;
}
$i ++;
}
&jcode::convert(\$Buff, "jis");
push(@SplitedWordList, $Buff);
for $SplitedWord (@SplitedWordList) {
$EncodedString .= '=?ISO-2022-JP?B?';
$BitStream = unpack("B*", $SplitedWord);
$i = 0;
while($chunk = substr($BitStream, $i*6, 6)) {
unless(length($chunk) == 6) {
$chunk = pack("B6", $chunk);
$chunk = unpack("B6", $chunk);
}
$ByteChunk = sprintf("%08d", $chunk);
$PackedByteChunk = pack("B8", $ByteChunk);
$DecimalNum = unpack("C", $PackedByteChunk);
$EncodedString .= substr($Base64Table, $DecimalNum, 1);
$i++;
}
if(length($SplitedWord) % 3 == 1) {
$EncodedString .= '==';
} elsif(length($SplitedWord) % 3 == 2) {
$EncodedString .= '=';
}
$EncodedString .= '?='."\n ";
}
$EncodedString =~ s/\n $//;
} else {
$EncodedString = $String;
}
return $EncodedString;
}
|
■変更履歴
2001/10/31 サブジェクトに「0」が含まれるとそれ以降の文字が無効になってしまうバグを解消。