2010-09-27T02:58:07の更新内容

programming/tips/quoted_printable/index.wiki.txt

current previous
1,225 0,0
+
${smdncms:title,quoted-printableのエンコード・デコード}
+
${smdncms:keywords,C#,quoted-printable,Qエンコード}
+
${smdncms:tags,api/.net,lang/c#}
+

          
+
C#でのquoted-printableのエンコード/デコード処理の実装。
+

          
+
-参考
+
--&urn2url(urn:ietf:rfc:2045);
+
--[[Quoted-printable - Wikipedia:http://ja.wikipedia.org/wiki/Quoted-printable]]
+
--[[Quoted-printable - Wikipedia, the free encyclopedia:http://en.wikipedia.org/wiki/Quoted-printable]]
+
-関連するページ
+
--[[works/libs/Smdn.Formats.Mime]] (.NET Framework用MIMEライブラリ)
+
--[[programming/tips/findmimetype]]
+
--[[programming/tips/modified_utf7]]
+
--[[programming/tips/urlunescape_netfx2]]
+

          
+
エンコード処理では、76文字程度で自動的に改行するようにしてある。
+

          
+
#code(cs){{
+
using System;
+
using System.IO;
+
using System.Text;
+

          
+
static class QuotedPrintable {
+
  // 16進文字への変換テーブル
+
  private static readonly byte[] hexChars = new byte[] {
+
    0x30, 0x31, 0x32, 0x33, // '0', '1', '2', '3'
+
    0x34, 0x35, 0x36, 0x37, // '4', '5', '6', '7'
+
    0x38, 0x39, 0x41, 0x42, // '8', '9', 'A', 'B'
+
    0x43, 0x44, 0x45, 0x46, // 'C', 'D', 'E', 'F'
+
  };
+

          
+
  // エンコード
+
  public static string Encode(string quoteString, Encoding encoding)
+
  {
+
    if (encoding == null)
+
      throw new ArgumentNullException("encoding");
+

          
+
    return Encode(encoding.GetBytes(quoteString));
+
  }
+

          
+
  public static string Encode(byte[] quoteBytes)
+
  {
+
    if (quoteBytes == null)
+
      throw new ArgumentNullException("quoteBytes");
+

          
+
    // とりあえず変換前の4倍の容量でMemoryStreamを作成
+
    using (var quoted = new MemoryStream(quoteBytes.Length * 4)) {
+
      var charcount = 0;
+

          
+
      foreach (var octet in quoteBytes) {
+
        if (73 < charcount) {
+
          // 次のエスケープで76文字を越える可能性がある場合、ソフト改行を入れる
+
          var escaped = false;
+

          
+
          quoted.WriteByte(0x3d); // '=' 0x3d
+

          
+
          if (octet == 0x09 || octet == 0x20) {
+
            // '\t' 0x09 or ' ' 0x20
+
            quoted.WriteByte(hexChars[(octet & 0xf0) >> 4]);
+
            quoted.WriteByte(hexChars[octet & 0x0f]);
+

          
+
            escaped = true;
+
          }
+

          
+
          quoted.WriteByte(0x0d); // '\r' 0x0d
+
          quoted.WriteByte(0x0a); // '\n' 0x0a
+

          
+
          charcount = 0;
+

          
+
          if (escaped)
+
            continue;
+
        }
+

          
+
        if ((0x21 <= octet && octet <= 0x3c) ||
+
            (0x3e <= octet && octet <= 0x7e) ||
+
            octet == 0x09 ||
+
            octet == 0x20) {
+
          // printable char (except '=' 0x3d)
+
          quoted.WriteByte(octet);
+

          
+
          charcount++;
+
        }
+
        else {
+
          // '=' 0x3d or control char
+
          quoted.WriteByte(0x3d); // '=' 0x3d
+
          quoted.WriteByte(hexChars[(octet & 0xf0) >> 4]);
+
          quoted.WriteByte(hexChars[octet & 0x0f]);
+

          
+
          charcount += 3;
+
        }
+
      } // foreach
+

          
+
      return Encoding.ASCII.GetString(quoted.ToArray());
+
    } // using
+
  }
+

          
+
  // デコード
+
  public static string Decode(string quotedString, Encoding encoding)
+
  {
+
    if (encoding == null)
+
      throw new ArgumentNullException("encoding");
+

          
+
    return encoding.GetString(Decode(quotedString));
+
  }
+

          
+
  public static byte[] Decode(string quotedString)
+
  {
+
    if (quotedString == null)
+
      throw new ArgumentNullException("quotedString");
+

          
+
    using (var decoded = new MemoryStream(quotedString.Length)) {
+
      var buffer = new byte[3];
+
      var bufferOffset = 0;
+

          
+
      foreach (byte octet in quotedString.ToCharArray()) {
+
        if (bufferOffset == 0) {
+
          if (octet == 0x3d) // '=' 0x3d
+
            // quoted
+
            buffer[bufferOffset++] = octet;
+
          else
+
            // non-quoted
+
            decoded.WriteByte(octet);
+
        }
+
        else {
+
          // quoted char
+
          buffer[bufferOffset++] = octet;
+
        }
+

          
+
        if (bufferOffset == 3) {
+
          // dequote
+
          if (buffer[1] == 0x0d && buffer[2] == 0x0a) {
+
            // soft newline ('\r' 0x0d + '\n' 0x0a)
+
            bufferOffset = 0;
+
          }
+
          else if (buffer[1] == 0x0d || buffer[1] == 0x0a) {
+
            // soft newline ('\r' 0x0d or '\n' 0x0a)
+
            if (buffer[2] == 0x3d) {
+
              bufferOffset = 1;
+
            }
+
            else {
+
              decoded.WriteByte(buffer[2]);
+
              bufferOffset = 0;
+
            }
+
          }
+
          else {
+
            byte d = 0x00;
+

          
+
            for (var i = 1; i < 3; i++) {
+
              d <<= 4;
+

          
+
              if (0x30 <= buffer[i] && buffer[i] <= 0x39)
+
                // '0' 0x30 to '9' 0x39
+
                d |= (byte)(buffer[i] - 0x30);
+
              else if (0x41 <= buffer[i] && buffer[i] <= 0x46)
+
                // 'A' 0x41 to 'F' 0x46
+
                d |= (byte)(buffer[i] - 0x37);
+
              else if (0x61 <= buffer[i] && buffer[i] <= 0x66)
+
                // 'a' 0x61 to 'f' 0x66
+
                d |= (byte)(buffer[i] - 0x57);
+
              else
+
                throw new FormatException("incorrect form");
+
            }
+

          
+
            decoded.WriteByte(d);
+

          
+
            bufferOffset = 0;
+
          }
+
        }
+
      } // foreach
+

          
+
      return decoded.ToArray();
+
    } // using
+
  }
+
}
+
}}
+

          
+
以下、使用例と実行結果。
+

          
+
#code(cs){{
+
using System;
+
using System.Text;
+

          
+
class Sample {
+
  public static void Main(string[] args)
+
  {
+
    Console.WriteLine(QuotedPrintable.Encode("漢字abcかな123カナ", Encoding.GetEncoding("iso-2022-jp")));
+
    Console.WriteLine(QuotedPrintable.Encode("漢字abcかな123カナ", Encoding.GetEncoding("euc-jp")));
+

          
+
    Console.WriteLine();
+
    Console.WriteLine(QuotedPrintable.Decode("=1B$B4A;z=1B(Babc=1B$B$+$J=1B(B123=1B$B%+%J=1B(B", Encoding.GetEncoding("iso-2022-jp")));
+
    Console.WriteLine(QuotedPrintable.Decode("=1B=24B4A=3Bz=1B=28Babc=1B=24B=24=2B=24J=1B=28B123=1B=24B=25=2B=25J5", Encoding.GetEncoding("iso-2022-jp")));
+
    Console.WriteLine(QuotedPrintable.Decode("Now's the time =\r\nfor all folk to come=\r\n to the aid of their country.", Encoding.ASCII));
+
    Console.WriteLine(QuotedPrintable.Decode("Now's the=\n time =\rfor all folk to come =\r\nto the aid=\r=\n of their country.", Encoding.ASCII));
+

          
+
    Console.WriteLine();
+
    Console.WriteLine(QuotedPrintable.Encode(@"The quick brown fox jumps over the lazy dog
+
The quick brown fox jumps over the lazy dog
+
The quick brown fox jumps over the lazy dog
+
The quick brown fox jumps over the lazy dog
+
The quick brown fox jumps over the lazy dog
+
The quick brown fox jumps over the lazy dog
+
The quick brown fox jumps over the lazy dog
+
The quick brown fox jumps over the lazy dog", Encoding.ASCII));
+
  }
+
}
+
}}
+

          
+
#prompt(実行結果){{
+
=1B$B4A;z=1B(Babc=1B$B$+$J=1B(B123=1B$B%+%J=1B(B
+
=B4=C1=BB=FAabc=A4=AB=A4=CA123=A5=AB=A5=CA
+

          
+
漢字abcかな123カナ
+
漢字abcかな123カナ
+
Now's the time for all folk to come to the aid of their country.
+
Now's the time for all folk to come to the aid of their country.
+

          
+
The quick brown fox jumps over the lazy dog=0D=0AThe quick brown fox jumps=20
+
over the lazy dog=0D=0AThe quick brown fox jumps over the lazy dog=0D=0ATh=
+
e quick brown fox jumps over the lazy dog=0D=0AThe quick brown fox jumps o=
+
ver the lazy dog=0D=0AThe quick brown fox jumps over the lazy dog=0D=0AThe=20
+
quick brown fox jumps over the lazy dog=0D=0AThe quick brown fox jumps ove=
+
r the lazy dog
+
}}
+