質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.33%

  • MacOS(OSX)

    2037questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

  • Visual Studio

    2005questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • Xamarin

    532questions

    Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

Mime規格に則ったメールのsubjectを作成する際に、Base64でエンコードする文字列の途中に記号があると文字列としてデコードされません。

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 728

xero_con

score 4

現状

(頂きました回答を元に、文末に追記致しました)

以下の環境で、メーラーを作成しているのですが。
subjectを作成する際にBase64へUTF-8の文字列をエンコードしています。
エンコードする文字列のなかに記号や半角文字が入った状態でエンコードしメールを送信すると、
受信したメーラーでsubjetがデコードされていない状態になってしまっています。
例と使用環境、現在のコードは以下に記載させていただきます。

何卒、ご教授の程よろしくお願い致します。

やりたいこと

subjectの文字列がしっかりとデコードされ、文章として読めるようになること。
(今後:文字列はUTF-8だけでなく、ASCIIやShift-JIS、iso-2022-jpなどを用いることも視野に入れています)
(今後:Base64だけでなく、quoted-printableでのエンコードも考えています)

使用環境

PC:MacOSX
環境:Visual Studio Community 2017 for Mac
言語:c#

現状

以下のコードでsubjectを作成しています。

string sub = "我輩は猫である。名前はまだ無い。";

MimeKit.Encodings.Base64Encoder ed = new MimeKit.Encodings.Base64Encoder();
System.Text.Encoding charEcd = System.Text.Encoding.UTF8;

sub = sub + "\n\r";    // 何故か末尾1文字が切れてしまうための対策
byte[] cc = charEcd.GetBytes(sub);
byte[] nnn = new byte[1000];   // outputされるバイト領域を多めに取っています

ed.Encode(cc, 0, cc.Length, nnn);

string ttt = System.Text.Encoding.UTF8.GetString(nnn);
m_subject = string.Format("=?{0}?B?{1}?=", charEcd.BodyName, ttt.TrimEnd('\0'));

Mimemes.Headers.Add(HeaderId.Subject, m_subject);

上記コード内(1行目)にある文字列が記載されている通りの場合は、以下の画像の成功例のようになります。
しかし、以下のようにすると失敗例の画像のようになってしまいます。

string sub = "吾輩は猫である。1.2.3.名前はまだ無い。"

どうすればしっかりとデコードされ、受信側メーラーの件名に
「吾輩は猫である。1.2.3.名前はまだ無い。」
と表示されるのでしょうか?

何卒ご教授のほど、よろしくお願い致します。

成功例と失敗例

①成功例画像
成功例

②失敗例画像
失敗例


追記(1)

文字列中にCRが混入しているとのコメントを頂き、以下のようにコードを編集致しました。

string sub = "あいうえおかきくけこさしすせそなにぬねのはひふへほ";

MimeKit.Encodings.Base64Encoder ed = new MimeKit.Encodings.Base64Encoder();
System.Text.Encoding charEcd = System.Text.Encoding.UTF8;

byte[] cc = charEcd.GetBytes(sub);
byte[] nnn = new byte[1000];
ed.Encode(cc, 0, cc.Length, nnn);

byte[] bb = charEcd.GetBytes("\n");
byte[] nb = new byte[1000];

int ii = 0;
for (int i = 0; i < nnn.Length;i++)
{
    if (nnn[i] != bb[0])
    {
        nb[ii] = nnn[i];
        ii = ii + 1;
    }
}

string ttt = charEcd.GetString(nb).TrimEnd('\0');
m_subject = string.Format("=?{0}?B?{1}?=", charEcd.BodyName, ttt);

新しく編集したコードでエンコードした文字列は以下となります
(エンコードした文字列はブレークポイントで停止し抽出したエンコードした文字列です)

- 元の文字列:あいうえおかきくけこさしすせそなにぬねのはひふへほ
- エンコードした文字列:=?utf-8?B?44GC44GE44GG44GI44GK44GL44GN44GP44GR44GT44GV44GX44GZ44Gb44Gd44Gq44Gr44Gs44Gt44Gu44Gv44Gy44G144G444G7?=

問題点

ネットなどにあるツールを用いると、元の文字列の通り「あいうえおかきくけこさしすせそなにぬねのはひふへほ」とデコードすることができます。
しかし実際にメールとして送信し、受信メーラー(複数メーラーにて確認済み)しすると、どれもデコードに失敗し、エンコードした文字列のまま表示されてしまっていました。

どのようにしたらメーラーのsubjectでエンコードした文字列を表示できるのでしょうか?

何卒、ご教授をお願い致します。

試したこと

エンコードした文字列が長すぎるのかとも思い、以下のように複数行へエンコードも実践致しました。

=?utf-8?B?44GC44GE44GG44GI44GK44GL44GN44GP44GR44GT44GV44GX44GZ44Gb44Gd?=
=?utf-8?B?44Gq44Gr44Gs44Gt44Gu44Gv44Gy44G144G444G7?=


しかし結果は問題点同様の結果しか得ることができませんでした。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+2

先日別の質問で提示させて頂いた、MimeKit/Rfc2047.csを一読されるようお勧めします。
EncodePhrase() または EncodeText()
-> Encode()
-> AppendEncodedWord()
あたりの流れが参考になると思います。

AppendEncodedWord()では、942行目の

if (CharsetRequiresBase64 (charset) || GetBestContentEncoding (word, 0, len) == ContentEncoding.Base64) {


でBase64/Quoted-printableを切り替えています。

これらの情報を参考に、必要な部分を抜き出せばやりたいことは実現出来ると思います。
(GetBestContentEncoding()の戻り値相当を、変更or指定出来れば良いのですが……)


検証に使えるsmtpサーバが無かったので、smtp4devを使って確認してみました。

            var subject = "吾輩は猫である。1.2.3.名前はまだ無い。";
            var encoded = Rfc2047.EncodePhrase(Encoding.UTF8, subject);

            var message = new MimeMessage();
            message.From.Add(new MailboxAddress("harq@foo"));
            message.To.Add(new MailboxAddress("harq@bar"));
            message.Subject = Encoding.ASCII.GetString(encoded);
            message.Body = new TextPart("plain") { Text = @"Hello World" };

            using (var smtp = new SmtpClient())
            {
                smtp.Connect("localhost", 25);
                smtp.Send(message);
                smtp.Disconnect(true);
            }


smtp4dev画面
smtp4dev画面
Viewの結果

From: harq@foo
Date: Sun, 04 Mar 2018 01:31:41 +0900
Subject: 
 =?utf-8?b?5ZC+6Lyp44Gv54yr44Gn44GC44KL44CCMS4yLjMu5ZCN5YmN44Gv44G+44Gg54Sh?=
 =?utf-8?b?44GE44CC?=
Message-Id: <NFBL3KIPS3U4.QV9M8QV27FCR3@xxxxxxxxx>
To: harq@bar
MIME-Version: 1.0
Content-Type: text/plain; charset=utf-8

Hello World


あとは受信側のメーラー次第かなと。


上では自前のencodeを実現するために細々とやりましたが、utf-8のBASE64ならsubjectへutf-8文字列を代入するだけで、あとは上手くやってくれたはず……。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/04 03:08

    ご回答をありがとうございます。
    いただきました助言も満足に生かせていなかった中、お見捨てになられなかっただけでも感謝しきれません。
    そしてさらにサンプルコードとテストまで行っていただき、お礼の言葉もございません。
    この度は本当にありがとうございました。

    お陰様で、Base64につきましては安定してsubjectをエンコードすることができるようになりました。

    quoted-printableに付きましては、現在は下記のようなコードを書いて試行錯誤しております。
    quoted-printableの形式にすることはできたのですが、下記コードですと文字列が自動的にASCIIに変更されてしまうという欠点があります。
    ひとまず、欠点解消のために、ご教授頂きましたMimeKit/Rfc2047.csをもう一度しっかりと
    読んでみようと思います。
    それでもどうしてもわからないようでしたら、改めて質問を掲載させていただこうと思います。
    (この質問ページで質問をすると、本質問からずれてしまうようで申し訳ないので・・・)

    この度はご丁寧に対応くださり、誠に有難うございました。

    ----
    string m_subject = "吾輩は猫である。1.2.31名前はまだ無い。";
    MimeKit.Encodings.QuotedPrintableEncoder ed = new MimeKit.Encodings.QuotedPrintableEncoder();

    System.Text.Encoding charEcd = System.Text.Encoding.UTF8;
    byte[] cc = charEcd.GetBytes(m_subject);
    byte[] nnn = new byte[1000];
    int a = ed.Encode(cc, 0, cc.Length, nnn);
    byte[] sn = new byte[a];
    for (int i = 0; i < a;i++)
    {
    sn[i] = nnn[i];
    }
    Mimemes.Subject = System.Text.Encoding.UTF8.GetString(sn);

    キャンセル

  • 2018/03/04 08:17

    MailKit/MimeKitはMimeMessageをSend時にEncodeしたと思うので、Quated-Printableの実現は(このままだと)難しいかも知れません。
    SubjectにQuated-Printableの結果を入れた場合、それをBASE64したus-asciiを送信しそうな気がします(そうなってますよね?)。
    何処かのclassを継承&オーバーライドすることで解決できれば良いのですが……。

    キャンセル

+1

手元で確認しますとMimeKit.Encodings.Base64Encoder.Encoderが72文字毎に改行を入れているようです。そのため、本来

=?utf-8?B?5ZC+6Lyp44Gv54yr44Gn44GC44KL44CCMS4yLjMu5ZCN5YmN44Gv44G+44Gg54Sh44GE44CC?=

となるべき所が

=?utf-8?B?5ZC+6Lyp44Gv54yr44Gn44GC44KL44CCMS4yLjMu5ZCN5YmN44Gv44G+44Gg54Sh44GE44CC
?=

となってしまっています。文字数が多ければ2行目に文字が入ってきます。

対応としては改行を全て無くしてしまうのが一番簡単かも知れません。ただ、本来はMIMEのヘッダーは1行がCRLFを除き78文字以下であるべき(SHOULD)という制約があるのですが、ほとんどのメーラーは78文字以上でも表示してくれます。

なお、72文字毎に改行しているのは、本文などをBASE64エンコードしたときはそのまま貼り付けることができるからだと思われます。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/03 23:49

    ご回答をありがとうございます。

    エンコードしたバイト配列内を見てみたところ、確かに72バイト目にCR(10)が入っていることが確認できました。ありがとうございます。

    早速、CRを取り除き、エンコードしたsubjectを作成したものが以下となります。
    「=?utf-8?B?44GC44GE44GG44GI44GK44GL44GN44GP44GR44GT44GV44GX44GZ44Gb44Gd44Gq44Gr44Gs44Gt44Gu44Gv44Gy44G144G444G7?=」
    元の文字列は「あいうえおかきくけこさしすせそなにぬねのはひふへほ」です。

    ネットなどにあるツールを用いてデコードすると、元の文字列へと変換できることが確認できます。
    その上でメールを送信したのですが、いざ受信したメーラーで見てみると、
    subjectsがデコードされず、「=?utf-8?B?44GC44GE44GG44GI44GK44GL44GN44GP44GR44GT44GV44GX44GZ44Gb44Gd44Gq44Gr44Gs44Gt44Gu44Gv44Gy44G144G444G7?=」のままの表示となったままでした。

    なぜメーラーだけデコード出来ない理由がわかりません。
    何かお心当たりや、解決法を御存知でしたらこ教授ください。
    何卒よろしくお願い致します。

    (本文に追記として、改変したコードを掲載しておきます。
    ご参考になれば幸いです)

    キャンセル

  • 2018/03/04 00:33

    メーラーでメールのソースを見ないとこれ以上はよくわかりません。メールをファイルとして保存して、勝手にMIMEデコードしないただのテキストエディタでヘッダ部分がどうなっているかを貼り付けてください。

    キャンセル

  • 2018/03/04 00:57

    それ以前にこれは quoted printable ではありませんね。

    キャンセル

  • 2018/03/04 00:58

    あ、失礼しました。quoted printable をやらされてるのは私だけでした。

    キャンセル

  • 2018/03/04 01:17

    メーラーだけデコードできないと言うのも事実と違います。私の回答に書いたように標準の方法でデコードできませんでした。
    最後の文字が切れるような訳の分からないものになぜ固執するのか疑問です。

    キャンセル

-1

何やらややこしいことをされていますが、次のソースで Base64 にエンコード・デコードできます。

using System.Text;

var encoded = Convert.ToBase64String(Encoding.UTF8.GetBytes("我輩は猫である。1.2.3.名前はまだ無い。"));
Console.WriteLine(encoded);
var decoded = Encoding.UTF8.GetString(Convert.FromBase64String(encoded));
Console.WriteLine(decoded);

またメーラーの問題の可能性もありますので、別のメーラーでも確かめてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/03/03 08:26

    ちなみに、失敗例で表示されているものをデコードした結果が次になります。

    吾輩は猫である。1.2.3?]前はまだ無い?YJ

    どう見てもエンコードに失敗していますね。

    今回あなたの失敗は二つ。
    1. 確認を怠ったこと。まずはエンコードしたものをデコードしてうまくできるかどうかの確認。また一つのメーラーだけでなく複数のメーラーを使っての確認。
    2.サードパーティーのライブラリを使ったこと。標準ライブラリにある機能はしかるべき相当の理由のない限り標準のものを使いましょう。

    キャンセル

  • 2018/03/03 09:05

    ご回答をありがとうございます。

    追記につきまして、こちらから少し説明させていただきます。

    1. お言葉を返すようで大変恐縮なのですが。
    複数のメーラーでの確認、またエンコードに失敗した文字列が形成されていることについての確認は確認済みでした。その上で、ご質問させていただきました次第です。

    2. こちらは私の説明不足で申し訳ございません。
    なぜこのようなコードになったかと申しますと、将来的にquoted-printableや8bitなどでのエンコードを行うことをふまえての結果です。
    標準ライブラリにquoted-printableへエンコードする機能が見つからなかったため、それならばBase64・quoted-printableの双方に対応したMimeKitを利用しようと思い立った次第です。

    最後に、
    ご回答いただきましたコードを元に以下のようなコードを作成いたしました。

    string sub = "吾輩は猫である。1.2.3名前はまだ無い。";
    var encoded = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(sub));
    m_subject = string.Format("=?{0}?B?{1}?=", System.Text.Encoding.UTF8.BodyName, encoded); // ここはsubjectの形式にあるように形成しています。

    誠に残念ながら、これもエンコードに失敗する例と同様の結果となってしまいました。
    何か誤りがあったのならばご指摘をお願い致します。

    そしてもし、さらに回答が可能なようでしたら、上記をふまえた上で
    1、quoted-printableにエンコードするにはどうすれば良いのか。(また標準ライブラリにその機能があるのか)
    2、エンコードに失敗する理由

    をご教授いただければと思います。
    何卒よろしくお願い致します。

    キャンセル

  • 2018/03/03 09:19

    確認済みならば報告してください。
    報告を怠ればこちらでも確認しなければならず二度手間です。

    今回もそうですが、Convert を使った例で「同様の結果」ではわかりません。
    どのような結果になりますか?
    メーラーでの画像ではこちらで打ち直さなければなりません。
    テキストで質問にソースと結果を追記してください。

    1.quoted-printable の変換は標準にはありません。
    サードパーティーのものを使っても構いませんが、自作でも容易(十分くらいで作れる程度)です。
    自作の方が手を入れやすくていいと思います。

    2.こちらでは失敗しないのでまだわかりません。
    どのように失敗するのか「失敗します」ではなく原因が究明できる形で報告してください。

    キャンセル

  • 2018/03/03 09:23

    https://ja.wikipedia.org/wiki/Quoted-printable
    Quoted-printable の仕様です。
    機械的に変換できると思います。

    キャンセル

同じタグがついた質問を見る

  • MacOS(OSX)

    2037questions

    MacOSとは、Appleの開発していたGUI(グラフィカルユーザーインターフェース)を採用したオペレーションシステム(OS)です。Macintoshと共に、市場に出てGUIの普及に大きく貢献しました。

  • Visual Studio

    2005questions

    Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

  • Xamarin

    532questions

    Xamarin(ザマリン)は、iPhoneなどのiOSやAndroidで動作し、C# 言語を用いてアプリを開発できるクロスプラットフォーム開発環境です。Xamarin Studioと C# 言語を用いて、 iOS と Android の両方の開発を行うことができます。

  • トップ
  • MacOS(OSX)に関する質問
  • Mime規格に則ったメールのsubjectを作成する際に、Base64でエンコードする文字列の途中に記号があると文字列としてデコードされません。