スキップしてメイン コンテンツに移動

Outlookは添付ファイル名をMIME-BエンコードするときとMIME-Qエンコードするときがある ~ OutlookでUTF-8を使ったら、iPhoneの標準メーラーで添付ファイル名が文字化けした話 ~

Outlook で Unicode (UTF-8) を使うときのお話として聞いてね。


あ、良かったらこちらもどうぞ

【Outlookで自動改行させないための設定 ~もういい加減 UTF-8 / base64 でよくね?~】

https://chibitcpu.blogspot.com/2025/12/outlook.html


RFCも絡めてまとめてみたので、サクッと対応したい人はこちら

【今日こそ終止符!iPhoneの標準メーラーで添付ファイル名が文字化けする原因とその対策】

https://chibitcpu.blogspot.com/2026/03/outlook-iphone-filename-encoding-5n.html


さて・・・

 iPhoneの標準メーラーで添付ファイル名が文字化けした。(正確には「復号してくれなかった」)

=?utf-8?Q?....

今までと同じ方法でファイルを添付して送っていたのに、なぜか表示されない。

そこで、表示できるときとできないときの違いを知りたくて、メールのソースを表示してみた。


すると

■ 表示できる方

filename="=?utf-8?B?..."


■ 表示できない方

filename="=?utf-8?Q?..."


できる方は 「B」エンコーディング (Base64)、できない方は 「Q」エンコーディング (Quoted-Printable っぽいやつ) だった。

おいおい、なんで違うんだよって思って、Copilotに聞いてみた。



Q. Outlook2019を使っています。ファイル名に日本語を含む添付ファイルを送るとき、ファイル名が「B」エンコーディングされるときと、「Q」エンコーディングされるときがあります。「B」エンコーディングで固定したいのですが、どうすればいいですか?


A. 結論から言うと、Outlook 2019 では添付ファイル名の符号化方式をユーザー側で固定する設定は存在しません。これは仕様であり、Outlook が内部ロジックに基づいて自動的に選択しているためです。ただし、どのような条件で 「B」/「Q」が選ばれるのかは不明です。ただ 、「B」エンコーディング に“寄せる”ために現実的にできる対策があります。


まじかよ。仕様かよ。

Base64に「寄せるため」には、日本語を多めにしろだって。


【2026.3.3 】

Outlookのエンコード仕様を確認したくて、ファイル名を変えながら添付して送って調べてみた。

漢字1文字(3バイト):半角5文字(5バイト)以上の比率だと「Q」エンコーディング 、未満だと「B」エンコーディングだった。

Quoted-Printableより、Base64の方が短い(エンコード後のバイト数が少ない)んだけど…なんでだろ

エンコードマニアックス


【2026.3.4 その1】

おもしろそう。あとで読もうっと。

MIMEの基礎


【2026.3.4 その2】

おぉ、そういうことか。日本人にしてみたら謎の(Base64の方がバイト数が少ないのに、なんでバイト数が増えるQuoted-Printableを使うの?)変換ルールは、ヨーロッパ系文字にあわせた仕様だったのね。

MIME - quoted-printable


添付ファイル名について調べてみた限り、Outlook の 「送信メッセージで優先使用するエンコード方法」による違いはこうだった。

・日本語 (JIS) ⇒ 「B」エンコーディング

・日本語 (JIS 1 バイト カタカナ可) ⇒ 「B」エンコーディング

・日本語 (シフト JIS) ⇒ 「B」エンコーディング

・Unicode (UTF-8) ⇒ 変換ルール

日本語は「B」エンコーディング (Base64) の方が効率いいけど、多言語も扱うUnicode(UTF-8)は変換ルールが採用されるようだ。


【2026.3.4 その3】

BとQの境界線を調べたよ。Copilotの回答にあった「内部ロジック」の正体はこれだった。

■ Outlookの添付ファイル名変換ルール

B < 5N ≦ Q ※ Nは非ASCII文字数。


ちなみに UTF-8 だけかと思ったけど、Latin-1 もこのルールだったよ。結局、こういうことだった。

~ Geminiの回答 ~

『「UTF-8(マルチバイト)」でも「Latin-1(シングルバイト)」でも共通して 5N だったのは、結局のところ 「8ビット目のフラグが立っている文字(= 非ASCII文字)を、いかに効率よく7ビットの網(メールサーバー)に通すか」 という、1970〜80年代から続くメールの基本設計に由来していたわけです。』


文字エンコード方法とOSのロケールの両方を見て、BとQのどっちが効率いいか判断してもらえると、『さすがMS先輩、Apple小僧とは違うね』と称賛されると思う。


【2026.3.6】

iPhone標準メーラーは、添付ファイル名が RFC2047 の「B」エンコーディングには対応してるけど、「Q」エンコーディングには未対応。新しいほうのRFC2231は対応してた。


//  添付ファイルに関する処理 抜粋
$filename = "あ.xlsx";

//  RFC2047 B は OK
$filename_encoded = mb_encode_mimeheader($filename, "UTF-8", "B");
$body  = "Content-Type: application/octet-stream; name=\"{$filename_encoded}\"\r\n";
$body .= "Content-Disposition: attachment; filename=\"{$filename_encoded}\"\r\n";
$body .= "Content-Transfer-Encoding: base64\r\n";

//  RFC2047 Q は NG
$filename_encoded = mb_encode_mimeheader($filename, "UTF-8", "Q");
$body  = "Content-Type: application/octet-stream; name=\"{$filename_encoded}\"\r\n";
$body .= "Content-Disposition: attachment; filename=\"{$filename_encoded}\"\r\n";
$body .= "Content-Transfer-Encoding: base64\r\n";

//  RFC2231 は OK
$filename_encoded = rawurlencode($filename);
$body  = "Content-Type: application/octet-stream;\r\n";
$body .= "Content-Disposition: attachment; filename*=UTF-8''{$filename_encoded}\r\n";
$body .= "Content-Transfer-Encoding: base64\r\n";

コメント

このブログの人気の投稿

Chatの「メッセージは投稿者によって削除されました」を非表示にする方法

Chrome拡張機能を自作してやってみよう! ♪できるかな できるかな ・・・ 無理ぽ (´・ω・`) iframeの中に、実際のメッセージのやり取りが表示されるので、 $(function(){ $('iframe[name^="spareFrame"]').contents().find('[data-is-tombstoned="true"]').hide(); }); って書いたけど An iframe which has both allow-scripts and allow-same-origin for its sandbox attribute can escape its sandboxing. って言われてダメだったよ・・・

cron で実行されたコマンドから出力されたメッセージをメールで送信する方法

本題に入る前に、まずは、sh/bash系のシェルで標準出力と標準エラー出力をリダイレクトする方法から。 現在使用中のシェルを確認するには、 # echo $SHELL とすれば確認できる。 その他、利用できるシェルを確認するには # cat /etc/shells とする。 ■リダイレクトについて commandコマンドが出力を伴うコマンドの場合、commandコマンドの出力をresult.txtへ出力するには # command > result.txt コマンドの実効結果を別のコマンドの入力値とする場合は、|(パイプ)でつなげる。 # command1 | command2 ■標準出力と標準エラー出力について ・標準出力 正常結果やコマンド実行途中に出力されるメッセージの出力先。 ・標準エラー出力 異常終了時のメッセージやエラーメッセージなど、ユーザーに気づいてほしいメッセージの出力先。 ■標準出力と標準エラー出力の両方をリダイレクトする 先のcommandコマンドのリダイレクト例のうち、result.txtへのリダイレクトは、標準出力をリダイレクトしている。そのため、標準エラー出力はリダイレクトされず、仮にcommandコマンドが標準エラー出力へメッセージを出力した場合は、result.txtではなくコンソールへ出力(表示)される。 標準出力と標準エラー出力の両方をリダイレクトして、result.txtへ出力するには、 # command > result.txt 2>&1 とする。 なお、上の例を省略なしで記述すると # command 1> result.txt 2>&1 となる。 この「1」「2」の番号について。 ・1:標準出力。通常はコンソール画面。 ・2:標準エラー出力。通常はコンソール画面。 となっている。 ちなみに、「0」は「標準入力」。通常はキーボードからの入力。 例:標準エラー出力を error.log へ出力する。標準出力はコンソールへ表示する。 # command 2> error.log 例:標準出力は result.log 、標準エラー出力は error.log へ...

cron で bash を使うまでのお話

おー、ほぼ一年ぶりの更新だ・・・ 普段、何気に設定していた cron なんですが、 「PATHは通っていないから、フルパス書いて」 「#!/bin/bash はお呪い」 っていう程度の認識しかなかった。 ので、一からお勉強。 まず、cron の シェル等を確認するには、cron実行ユーザーで [root@localhost ~]# crontab -e * * * * * printenv >/var/tmp/env.txt ってやって1分待つ。 で、1分後に出来上がったファイルの中身を見てみる [root@localhost ~]# cat /var/tmp/env.txt ... SHELL=/bin/sh USER=root PATH=/usr/bin:/bin PWD=/root LANG=ja_JP.UTF-8 SHLVL=1 HOME=/root LOGNAME=root XDG_RUNTIME_DIR=/run/user/0 ... あー、shだ。 どおりで、/bin/bash って書かないと、動かない記述があるわけだ。