Quoted Printable とは
QuotedPrintable(QP encoding) とはエンコード方式の1種です。Base64 と同じように、データを印字可能なデータ(Ascii文字列)にエンコードします。メールのエンコード等で使われています。
QuotedPrintableは、任意の1バイトを "=" の後ろにそのバイトを表す16進数2桁 =FF
の形にエンコードします。
特徴
QuotedPrintableは、Base64と同じように印字可能な文字(Ascii文字)にエンコードしますが、Base64と違い印字可能なAscii文字はそのままにします。また、印字可能なAscii文字についてはこの変換を行わないため、元データがAscii文字を多く含む場合、エンコードされた文字列をほぼそのまま読むことができ、エンコード後のデータサイズも小さくなります。。
一方、元データにAscii文字をあまり含まないデータについては、エンコード後の文字列はほぼ意味をなさない文字列になることに加え、データサイズも非常に大きくなってしまいます。
これは、Base64が一律のデータ効率(約33%増)となる点と比べ、大きく異なる点です。
アルゴリズムと仕様
QuotedEncodingは、メールのエンコーディングに使われるため Content-Transfer-Encoding(MIME) として定義されています。上記URLはその定義文書です。
上記文書を参考にエンコードのルール以下に記します。
- 任意の1バイト(8ビット)は、"=" の後に続く2桁の16進数で表します。16進数は大文字を使います。
- 例:
=3D
- 例:
- 0x21("!")から0x3C("<")まで、および 0x3E(">")から 0x7E("~") は符号化せずそのままにする。
- 0x3D("=") は符号化します。
- 0x09(タブ文字) と 0x20(スペース) は符号化しません。ただし改行直前に来る場合は符号化します。
- 改行は CRLF を使います。テキストの場合、CR(0x0D)とLF(0x0A)は符号化しません。
- エンコードされたテキストは1行あたり76文字以下でないといけません。76文字を超える行はソフト改行(=CRLF)を入れます。
- ソフト改行とは元データにとって無意味な改行、つまりエンコードの仕様上挟まれる改行のことです。
- =と改行(CRLF)の間のスペースとタブはデコード時に無視されます。
- 改行直前の意味のあるタブとスペースは 3. にある通り、符号化しておく必要があります。
サンプルコード
C#
public string Encode(string text, Encoding encoding)
{
var sb = new StringBuilder();
// 対象文字列のバイトデータ取得
var bytes = encoding.GetBytes(text);
// 1行当たりの文字数カウンタ
var characterCount = 0;
for (int i = 0; i < bytes.Length; i++)
{
var octet = bytes[i];
// =00 の形に変換するかどうか
var isConverting = false;
switch (octet)
{
case 0x09: // タブ
case 0x20: // スペース
// 次に改行(CRLF)が続く場合は変換
// それ以外はそのまま出力
isConverting = i < bytes.Length - 2
&& bytes[i + 1] == 0x0D
&& bytes[i + 2] == 0x0A;
break;
case 0x0A: // LF
case 0x0D: // CR
// 変換せずそのまま出力
isConverting = false;
break;
default:
// 次の範囲の文字は変換して出力
// 31 0x1f US(ユニット区切り)以前の制御文字
// 127 0x7f DEL(削除) 以降の文字
// 61 0x3d =
isConverting = octet <= 0x20 || 0x7f <= octet || octet == 0x3d;
break;
}
// 出力文字列に追加
if (isConverting)
{
sb.Append($"={octet.ToString("X2")}");
characterCount += 3;
}
else
{
sb.Append((char)octet);
characterCount += 1;
}
// エンコードされた行の長さが76文字以下になるようにする
// 次の出力で "=00" と3文字出力されて77文字以上になるときはソフト改行
if (characterCount > 73)
{
sb.Append("=\r\n"); // =CRLF
}
// 改行されていれば文字数カウンタリセット
if (sb.ToString().EndsWith("\r\n"))
{
characterCount = 0; // カウンタリセット
}
}
return sb.ToString();
}
public string Decode(string text, Encoding encoding)
{
var bytes = new List<Byte>();
// ソフト改行をすべて削除
var reg = new Regex("=[ \t]*\r\n");
text = reg.Replace(text, string.Empty);
// 次に読む文字位置
var index = 0;
while (index < text.Length)
{
var c = text[index];
if (c == '=')
{
// = が出たら次の2文字を合わせて読む
// 範囲外参照になる場合は不正なQP
var octetStr = text.Substring(index + 1, 2);
// 16進数文字列としてByte型に変換
var octet = Convert.ToByte(octetStr, 16);
bytes.Add(octet);
// 詠み込んだ2文字すすめる
index += 2;
}
else
{
// = で始まらない文字はそのまま出力
// Ascii文字なのでByte型にキャスト
bytes.Add((byte)c);
}
index++;
}
var result = encoding.GetString(bytes.ToArray());
return result;
}