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

投稿

ラベル(文字コード)が付いた投稿を表示しています

そもそも添付ファイル名の規約ってRFCでどうなってるの?

Outlookから送ったメールの添付ファイル名が、iPhoneで「 =?utf-8?Q?...?= 」と無残に文字化けしてしまう現象。その元凶を紐解くと、1990年代から続く 「RFC規格のねじれ」 と、Microsoftによる 「独自の割り切り」 という深い闇に突き当たります。 前回の検証記事( Outlookのエンコード選択 / iPhoneでの5nの法則 )を補完する形で、そもそもRFC(インターネット標準)では添付ファイル名がどう定義されているのかを解説します。 1. 添付ファイル名を定義する「RFC 2183」 メールにファイルを添付する際、ヘッダーには Content-Disposition というフィールドが使われます。これを定義しているのが RFC 2183 です。 Content-Disposition: attachment; filename="example.txt" ここで重要なのは、このRFC 2183が制定された1997年当時、 filename パラメータには 「7bitのASCII文字(英数字)」しか想定されていなかった という点です。日本語などのマルチバイト文字をどう扱うかは、この規格内には書かれていません。 2. 日本語対応の救世主「RFC 2231」 そこで、ファイル名に日本語を使ったり、長すぎる名前を分割したりするために作られたのが RFC 2231 です。これが現在、世界標準の「正しい作法」です。 特徴: filename*=utf-8''%... という形式を使い、文字コードを明示する。 利点: ヘッダーの文法を壊さずに、安全に非ASCII文字を伝送できる。 3. なぜ問題が起きるのか? ― Outlookの「不採用」 ここが最大のポイントですが、 デスクトップ版のOutlook(2019等を含む)は、この「正しい作法(RFC 2231)」を頑なに採用していません。 代わりにOutlookが何をしているかというと、本来はメールの件名(Subject)などに使うための規格である RFC 2047(Encod...

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

OutlookからiPhoneにメールを送ると、添付ファイル名が =?UTF-8?Q?...?= と化ける問題。長年「相性の問題」で片付けられてきたこの怪奇現象に、休日の半日を潰してトドメを刺してきました。 1. 境界線の発見:「5Nの法則」 実験の結果、ファイル名に日本語(非ASCII)が混じっている場合、ある一定の条件でエンコード方式が切り替わることが分かりました。それが以下の 「5Nの法則」 です。 5Nの法則: 非ASCII文字を N 個含むとき、ASCII文字(英数字)が 5N 個に達した瞬間にiPhoneで文字化けが発生する。 非ASCII数 (N) Base64限界 (ASCII) 文字化け発動 (ASCII) 1文字 (あ) 4文字 5文字以上 2文字 (あい) 9文字 10文字以上 3文字 (あいう) 14文字 15文字以上 この法則はUTF-8だけでなく、欧州圏の iso-8859-1 (Latin-1) でも全く同様に発動しました。 2. なぜ「5」なのか?数学的背景 Outlookはメールヘッダーを1バイトでも節約しようと、以下の2つのエンコードを動的に使い分けています。 MIME-B (Base64): 全体を1.33倍にする。 MIME-Q (Quoted-...

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に「寄せ...

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

Outlookで勝手に「自動改行」させないための設定、正解は、[ファイル] - [オプション] - [詳細設定] の「文字設定オプション」で 「送信メッセージで優先使用するエンコード方法( E ):」を『 Unicode (UTF-8) 』にする 。 こうすることで、テキスト形式のメールは Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 として送信されるので、受信側でメールを表示すると  閲覧環境にあわせて適切に「折り返し」  送信者の意図した位置で「改行」 される。 送ったメールの「ソース」を受信側で表示するとこんな感じ。 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: base64 Content-Language: ja 44CA44KE44GB44CBDQrjgIDjgZPjgozjgpLoqq3jgpPjgafjgYTjgovjgajjgYTjgYbjgZPjgajj ga/jgIFiYXNlNjTjgqjjg7PjgrPjg7zjg4njgZXjgozjgZ/mloflrZfliJfjgpLjg4fjgrPjg7zj g4njgZfjgZ/jga7jga3jgIHjgZnjgbDjgonjgZfjgYTvvIENCg== base64エンコードすると、76文字で「改行」していることがわかるよね。 っていうかさ、違うんだよ。いつも、もやーっとするのね。 『Unicode (UTF-8)』って、Unicode という「符号化文字集合(character set)」を UTF-8 という「文字符号化方式」で「エンコード」してるんであって、「メールのエンコード方法」じゃないんだよね。 本来なら 符号化文字集合は何を使いますか? 文字符号化方式は何を使いますか? メールのエンコードはどうしますか? と3つ聞かないといけないんだけど、メールのヘッダには「符号化文字集合」を表すヘッダがないから(というか、世の中そうなんだけど)、1.と2.が「セット(UTF-8と言えばUnicode)」になってて、3.はそれに従う(8ビット...

mb_send_mail()するならReturn-Pathを第5引数に

mb_send_mail()を使ってメールを送信する場合は、第5引数に Return-Path を指定する。 さもなければ、Return-Path は apache@hoge.dom (コマンドラインから実行したときは、実行アカウント名@hoge.dom)になる。 第5引数に Return-Path を指定する方法はこんな感じ <?php // 日本語だよって宣言して mb_language('ja'); // UTF-8で組んでるなら、文字エンコードをUTF-8に合わせて mb_internal_encoding("UTF-8"); // ヘッダーとReturn-Pathを指定して、mb_send_mail() mb_send_mail("dare@doko.koko", "メールの送信テスト", "これは、メールの送信テストです。", "From:ore@example.dom", "-f ore@example.dom"); 問い合わせフォームなんかでメールアドレスを入力させるインターフェイスがある。 問い合わせ者へ受付メールを送信するのが一般的だが、もし、問い合わせ者がメールアドレスを入力ミスした場合、550 unknown user の送信先は Return-Path になるため、正しく指定しないと誰も間違いに気づかない。 まぁ、問い合わせ先の担当者が、問い合わせ者が入力したメールアドレスへメールを送れば気づくけどね。 どうせなら、最初から気づいた方がいいと思うけど。

mb_send_mail() するなら mb_convert_encoding() は不要

おれも勘違いしていたが、mb_send_mail()を使ってメールを送るとき、mb_convert_encoding()使って、ISO-2022-JPへ変換しなくていい。 具体的には、たったこれだけ。 <?php // 日本語だよって宣言して mb_language('ja'); // UTF-8で組んでるなら、文字エンコードをUTF-8に合わせて mb_internal_encoding("UTF-8"); // mb_send_mail()するだけ mb_send_mail("dare@doko.koko", "メールの送信テスト", "これは、メールの送信テストです。"); これだけで、 件名は、mb_encode_mimeheader(mb_convert_encoding("件名", "ISO-2022-JP", "UTF-8"), "ISO-2022-JP") してくれるし 本文は、mb_convert_encoding("本文", "ISO-2022-JP", "UTF-8") してくれる。 ただ、Fromに日本語使いたいときだけは、自前で $from = mb_encode_mimeheader(mb_convert_encoding("おれから", "ISO-2022-JP", "UTF-8"), "ISO-2022-JP")."<ore@doko.soko>"; ってやってから mb_send_mail("dare@doko.koko", "メールの送信テスト", "これは、メールの送信テストです。", "From:".$from); って送ってあげる。

htmlspecialchars()とhtmlentities()の第4引数はfalseで

文字エンコーディングに UTF-8 を使っているときは気にしなかったが、 EUC-JP や Shift_JIS を使わなければならなかったとき、ハマッた。 EUC-JP や Shift_JIS (両方とも、Windows標準キャラクタセットが扱える CP51932、SJIS-win を含む)の場合、これらの文字セットに存在しない ⓒ などをPOSTすると、数値文字参照形式でPOSTされる。 なので、POSTされた値を htmlspecialchars() や htmlentities() を通して echo htmlspecialchars(POST値, ENT_QUOTES, 文字エンコーディング); echo htmlentities(POST値, ENT_QUOTES, 文字エンコーディング); とすると、数値文字参照の「&」が「&amp;」に変換され、数値文字参照の値がそのまま表示される。 これは、両関数の第4引数「double_encode」のデフォルトが「true」のため。 ■PHPマニュアル htmlspecialchars() http://www.php.net/manual/ja/function.htmlspecialchars.php ■PHPマニュアル htmlentities() http://www.php.net/manual/ja/function.htmlentities.php 回避策は、この第4引数「double_encode」を「false」にしてやればいい。 こんな感じ。 echo htmlspecialchars(POST値, ENT_QUOTES, 文字エンコーディング, false); echo htmlentities(POST値, ENT_QUOTES, 文字エンコーディング, false);

HTMLエンティティ

久しぶりの記事更新。 UTF-8,Shift_JIS,EUC-JPで次のコードを実行したら、それぞれ結果が異なった。 <!DOCTYPE html> <html> <head> <meta charset="それぞれのcharset"> <title>HTMLエンティティ</title> </head> <body> <form method="post"> <?php $val = isset($_POST['val']) ? $_POST['val'] : ''; ?> <p>入力値:<input type="text" name="val"  value="<?=htmlentities($val, ENT_QUOTES, 'それぞれのcharset', false)?>" /></p> <p><input type="submit" value="GO" /></p> <?php echo "ポスト:{$val}"; // XSS脆弱性があるので注意。 ?> </body> </html> で、それぞれのエンコードの結果はこれ。 ■chrome ・UTF-8 入力値:© // ソースを表示すると、&copy; ポスト:© // ソースを表示すると、© ・Shift_JIS 入力値:© // ソースを表示すると、&#169; ポスト:© // ソースを表示すると、&#169; ・EUC-JP 入力値:© // ソースを表示すると、© ポスト:© // ソースを表示すると、© ■IE ・UTF-8 入力値:© // ソースを表示...

照合順序が latin1_swedish_ci なら おれも latin1

PHPが記述されたファイルのエンコード:EUC-JP (CP51932) MySQLのフィールドの照合順序:latin1_swedish_ci <? header("Content-Type: text/html; charset=EUC-JP"); $link = mysql_connect("localhost", "hoge", "hoge"); mysql_select_db("hoge", $link); mysql_set_charset("latin1", $link); mysql_query("INSERT INTO `tbl` (`value`) VALUES ('あ')", $link); $query = mysql_query("SELECT `value` FROM `tbl`", $link); $row = mysql_fetch_array($query, MYSQL_ASSOC); echo $row["value"]; mysql_free_result($query); mysql_close($link); ?> クライアント側(PHP)が EUC-JP だからといって mysql_set_charset("ujis", $link); とすると、MySQLが ujis (EUC-JP) から latin1 へ変換するので文字化けする。 そりゃね、latin1の文字セットに日本語の「あ」なんてないさ。 だから、クライアント側(PHP)も「latin1だよー」って言ってあげると、MySQLは変換しないので文字化けしない。 ということで、このサンプルの出力(echo $row["value"])の結果は あ となる。

charaset=EUC-JP

サンプルコードを張っておくよ。 テキストエディタでEUC-JPで保存してね。 サンプルを確認したサーバー環境: ・PHP Version 5.3.2-1ubuntu4.14 サンプルを確認したOSとブラウザ: ・XP で IE8、Google Chrome ・Ubuntu 10.04 で Google Chrome <? $chr = ""; $hex = ""; if (isset($_POST["check_char"])) {     //  ポストされた文字を16進に変換     $chr = $_POST["check_char"];     $arr = unpack("C*", $chr);     $hex = "";     for ($j = 1; $j <= count($arr); $j++) {         $hex.= base_convert($arr[$j], 10, 16);     } } ?> <html> <head> <meta http-equiv="content-language" content="ja" /> <meta http-equiv="content-type" content="text/html; charset=EUC-JP" /> <title>EUC-JP</title> </head> <body> <form action="" method="post" accept-charset="EUC-JP"> <p>チェックする文字:<input type="text" name="check_char" value="<?=$chr?>" /></p> <p>ポストさ...

Internet Explorer は 3バイトEUC-JP を表示できない

Firefox も Google Chrome も EUC-JP の3バイト文字を表示できるのに対し、 Internet Explorer は表示できない。 Internet Explorer で EUC系の文字をちゃんと表示しようと思ったら、 PHPだと、CP51932でエンコーディングしてあげる。 // CP932な文字をCP51932へエンコード。"伹" は 0xF9B2 $ecuchar = mb_convert_encoding("伹", "CP51932", "SJIS-win"); // CP932な文字をEUC-JP、eucJP-winへエンコードすると、 // 3バイト文字にエンコードされる文字があるので注意。 // CP932な文字をEUC-JPへエンコード。"伹" は 0x8FB0E3 $ecuchar = mb_convert_encoding("伹", "EUC-JP", "SJIS-win"); // CP932な文字をeucJP-winへエンコード。"伹" は 0x8FB0E3 $ecuchar = mb_convert_encoding("伹", "eucJP-win", "SJIS-win"); 2012/05/15 追記 ポストするときは、2バイト文字でポストするのにね。 charset=EUC-JP

文字コードのお話

書きかけですが...というか、俺のメモ。 奥が深い... ■文字集合・文字セット (character set) 文字を重複なく集めたもの。 ■符号化文字集合 (coded character set、CCS) ASCII 7bit。 0x5c:バックスラッシュ 0x7e:チルダ JIS X 0201 ラテン文字用図形文字集合と片仮名用図形文字集合のふたつの文字集合よりなっている。 8bit。 0x5c:円記号 0x7e:オーバーライン ※JIS X 0201-1997 において、文字名称がOVER LINEと規定されているが、図形としてはオーバーラインでなくチルダのような形をとっても良いことが附属書2において記されている。OADG109キーボードでは、オーバーラインが刻印されているが、OADG109Aキーボードでは、チルダが刻印されている。 JIS X 0208:1997 非漢字、第一水準漢字、第二水準漢字 JIS X 0212-1990 補助漢字。この文字集合が単独で使われることはない。JIS X 0208 を拡張する目的で策定された。 JIS X 0213:2000 面区点番号で表される。第一面には、JIS X 0208:1997がすべてと、JIS X 0208:1997の保留領域であった空き領域に、非漢字(659字)、第三水準漢字(1,249字)が収録されている。 第二面には、第四水準漢字(2,436字)が収録されている。 JIS X 0213:2004 JIS X 0213:2000に対して、例示字形の変更、10文字追加が行われた。例示字形の変更に対応したフォントが必要。 JIS X 0221 国際符号化文字集合。UCSのJIS規格。 Unicode 元々は、16bit固定の符号化文字集合。足らなくなったので21bitに拡張。その際、当初の16bit領域を「基本多言語面(BMP)」とした。この領域は、UCS-2で定義される範囲と同一。 ISO/IEC 10646 (UCS; Universal Coded Character Set) UCS-2 2オクテット(16bit)固定。UnicodeのBMP。 UCS-4 4オクテット(32bit)を基本とする。 ...