C#
Quoted-Printable
バイト配列をQuoted-Printable形式のASCIIテキストに変換
public static string ToQuotedPrintableString(byte[] inArray, int offset, int length)
{
if (inArray == null)
{
return null;
}
const string HexdecimalNumbers = "0123456789ABCDEF";
const ulong CrLfMask = 0xffff;
const ulong CrLfCode = 0x0d0a;
StringBuilder Builder = new StringBuilder();
ulong ByteSeq = 0;
int LineLength = 0;
Action<int> SoftLineBreakIfNeeded = (int CharCount) =>
{
if (LineLength + CharCount > 75)
{
Builder.Append("=\r\n");
LineLength = 0;
}
};
Action<byte> AppendAsQuoted = (byte Value) =>
{
SoftLineBreakIfNeeded(3);
Builder.Append('=');
Builder.Append(HexdecimalNumbers[Value >> 4]);
Builder.Append(HexdecimalNumbers[Value & 0x0f]);
LineLength += 3;
};
Action PreHardLineBreak = () =>
{
switch (ByteSeq & 0xff0000)
{
case 0x090000:
case 0x200000:
Builder.Length--;
LineLength--;
AppendAsQuoted((byte)((ByteSeq >> 16) & 0xff));
return;
}
if (LineLength < 2)
{
int _Count = LineLength + 2;
if (Builder.Length > _Count)
{
if (((ByteSeq >> (_Count << 3)) & CrLfMask) != CrLfCode)
{
Builder.Remove(Builder.Length - 1 - _Count, 3);
}
}
}
};
int Index = offset;
int Limit = offset + length;
while (Index < Limit)
{
byte CurByte = inArray[Index++];
ByteSeq <<= 8;
ByteSeq |= CurByte;
if ((ByteSeq & CrLfMask) == CrLfCode)
{
Builder.Length -= 3;
LineLength -= 3;
PreHardLineBreak();
Builder.Append("\r\n");
LineLength = 0;
}
else if ((CurByte == 0x09) ||
((CurByte >= 0x20) && (CurByte <= 0x3c)) ||
((CurByte >= 0x3e) && (CurByte <= 0x7e)))
{
SoftLineBreakIfNeeded(1);
Builder.Append((char)CurByte);
LineLength++;
}
else
{
AppendAsQuoted(CurByte);
}
}
ByteSeq <<= 16;
PreHardLineBreak();
return Builder.ToString();
}
public static string ToQuotedPrintableString(byte[] inArray)
{
return ToQuotedPrintableString(inArray, 0, inArray.Length);
}
Quoted-Printable形式のASCIIテキストをバイト配列に変換
public static byte[] FromQuotedPrintableString(string s)
{
if (s == null)
{
return null;
}
s = Regex.Replace(s, "[\t ]+(\r?)$", "$1", RegexOptions.Multiline);
s = s.Replace("=\r\n", string.Empty);
using (MemoryStream TempStream = new MemoryStream())
{
const string HexdecimalNumbers = "0123456789ABCDEF";
int Index = 0;
Action<int> TransferTo = (int Limit) =>
{
while (Index < Limit)
{
TempStream.WriteByte((byte)(s[Index++]));
}
};
Match m = Regex.Match(s, "=[" + HexdecimalNumbers + "]{2}");
while (m.Success)
{
TransferTo(m.Index);
int HighNibble = HexdecimalNumbers.IndexOf(m.Value[1]);
int LowNibble = HexdecimalNumbers.IndexOf(m.Value[2]);
TempStream.WriteByte((byte)((HighNibble << 4) | LowNibble));
Index += m.Length;
m = m.NextMatch();
}
TransferTo(s.Length);
return TempStream.ToArray();
}
}
Base64はConvert.ToBase64String()とConvert.FromBase64String()で変換できるが、Quoted-Printableは .Net Framework のライブラリにないようだ。電子メールを読み取るときに、Quoted-Printableを読み取る機能が必要になった。
Convert.ToBase64String()とConvert.FromBase64String()の仕様に似せた。
Quoted-Printableのルールは、RFC 2045の6.7章に記載されている。
(2021/03/05 初稿)
(2021/03/07 更新)