二进制到文本编码/解码类






4.24/5 (31投票s)
2004 年 3 月 16 日
6分钟阅读

363182

11518
一个 .NET 类库,用于处理 QuotedPrintable、UUEncode、Base64 和 Yenc 算法的编码/解码。
引言
本文介绍了一个用于在 .NET 中对文件和/或文本进行多种算法编码/解码的类库。此库的一些特性包括
- 对 Quoted Printable 格式的文本进行编码/解码
- 对 Base64 格式的文件和文本进行编码/解码
- 对 UUEncode 格式的文件和文本进行编码/解码
- 对 yEnc 格式的文件进行编码/解码
使用代码
请记住在您的项目中添加对 TextCoDec.dll 的引用。添加引用后,Visual Studio .NET 应该会自动为您处理复制工作。
Dim Yenc As New TextCodec.Yenc
Dim Parts() As String
'encode the windows calculator in 3 parts with a line length of 80
Parts = Yenc.Encode("C:\WINDOWS\system32\calc.exe", 80, 2, _
TextCodec.Yenc.yencVersion.Version1_1)
'decode to c:\
Yenc.Decode(Parts, "c:\", 0)
'test it
Shell("c:\calc.exe")
算法
Base64
Base64 编码/解码是最容易实现的:它是框架的一部分(System.Convert.ToBase64String()
和 System.Convert.FromBase64String()
)。请参阅文档。
Quoted Printable
此算法主要用于对非英语文本进行编码。超出 32 至 126 范围的字符代码将被转换为其 ASCII 十六进制值,并在前面加上等号(=)。例外情况是字符代码 61(等号)也必须进行编码。
For i = 0 To Chars.Length - 1
Ascii = Asc(Chars(i))
If Ascii < 32 Or Ascii = 61 Or Ascii > 126 Then
EncodedChar = Hex(Ascii).ToUpper
If EncodedChar.Length = 1 Then EncodedChar = "0" & EncodedChar
ReturnString.Append("=" & EncodedChar)
Else
ReturnString.Append(Chars(i))
End If
Next
UUEncode
我找到的最佳算法定义如下,摘自 此处。
uuencode 算法围绕 3 字节到 4 字节(8 位到 6 位数据)的编码进行,以将所有数据转换为可打印字符。要执行此编码,请从要编码的文件中读取 3 个字节,其二进制表示为
a7a6a5a4a3a2a1a0 b7b6b5b4b3b2b1b0 c7c6c5c4c3c2c1c0
并将它们转换为 4 个值在 0-63 范围内字节,如下所示:
0 0 a7a6a5a4a3a2 0 0 a1a0b7b6b5b4 0 0 b3b2b1b0c7c6 0 0 c5c4c3c2c1c0
然后,通过加上 0x20(32)将这些字节转换为可打印字符。
例外:如果您得到一个零字节,则应将其转换为 0x60(反引号 '`'),而不是 0x20(空格 ' ')。
此外,编码的开始由行“start”标记,其中 包含 3 个八进制数字,即文件的 UNIX 模式,以及 是原始文件名。编码的结束由行“end”标记。每行的第一个字符包含*原始文件*中的字节行长度,以与普通字节相同的方式编码,即行长度 0->0x60,所有其他长度加上 0x20 以转换为可打印字符。行长度范围从 0 到 45(编码为 'm';这就是为什么 uuencoded 文件中的行总是以 m 开头),在编码文件中是 61 个字符(包括长度字符)的行长度。这是通过电子邮件传输的一个安全长度。
编码文件中的行总是 4 的倍数 + 1 个字符长;这有时意味着在解码结束时会丢弃 1 或 2 个字节。
VB 中的主要编码是通过以下代码实现的:
For i = 0 To Chars.Length - 1 Step 3
DecodedBytes(0) = Asc(Chars(i))
DecodedBytes(1) = Asc(Chars(i + 1))
DecodedBytes(2) = Asc(Chars(i + 2))
EncodedBytes(0) = (DecodedBytes(0) \ 4 + 32)
EncodedBytes(1) = ((DecodedBytes(0) Mod 4) * 16) + _
(DecodedBytes(1) \ 16 + 32)
EncodedBytes(2) = ((DecodedBytes(1) Mod 16) * 4) + _
(DecodedBytes(2) \ 64 + 32)
EncodedBytes(3) = (DecodedBytes(2) Mod 64) + 32
If (EncodedBytes(0) = 32) Then EncodedBytes(0) = 96
If (EncodedBytes(1) = 32) Then EncodedBytes(1) = 96
If (EncodedBytes(2) = 32) Then EncodedBytes(2) = 96
If (EncodedBytes(3) = 32) Then EncodedBytes(3) = 96
ReturnString.Append(Chr(EncodedBytes(0)))
ReturnString.Append(Chr(EncodedBytes(1)))
ReturnString.Append(Chr(EncodedBytes(2)))
ReturnString.Append(Chr(EncodedBytes(3)))
Next
Yenc
本质上,yenc 算法可以通过以下表达式实现:
EncodedCharacter = (Character + 42) Mod 256
EncodedSpecialCharacter = (EncodedCharacter + 64) Mod 256
像往常一样,有一些字符构成例外。这些是空字符(0)、换行符(LF)、回车符(CR)和等号(=)。制表符也曾是一个例外,但在 1.2 版本中已删除。如果编码后的字符是上述字符之一,则使用 EncodedSpecialCharacter 表达式重新编码它,并用等号进行转义。
然而,yenc 算法非常灵活。如果出于某种原因,某个字符在编码流中不合适,则像对待特殊字符一样对其进行转义。这对于 nntp 传输特别有用。对于后者协议,双点(..)表示流的结束。然而,点字符默认不是 yenc 的特殊字符,因此您可能会得到一行以双点开头。这会使一些新闻阅读器混淆;一个好的原则是,如果点位于行首,则始终对其进行转义。
还有另一个关于行长度的例外。行长度的选择是灵活的,但它的长度也是可变的,因为您不能以转义字符结束一行。如果最后一个要编码的字符恰好是一个特殊字符,则按正常方式对其进行转义,最终得到两个字符(转义字符和编码后的字符),因此行长度为 length+1。
有关 yenc 的更多信息,请访问 www.yenc.org
VB 中的主要编码是通过以下代码实现的:
For i = 0 To n - 1
CharCode = (Bytes(i) + 42) Mod 256
Select Case CharCode
Case 0, 13, 10, 61
OutputLine &= "=" & Chr((CharCode + 64) Mod 256)
Case Else
If Version = yencVersion.Version1_1 And CharCode = 9 Then
OutputLine &= "=" & Chr((CharCode + 64) Mod 256)
Else
OutputLine &= Chr(CharCode)
End If
End Select
If OutputLine.Length >= LineLength Then
Output.Append(OutputLine & vbCrLf)
OutputLine = ""
End If
Next
流
当我从头重写代码时,我惊叹于流让我的生活变得多么轻松。不仅如此,代码的速度也得到了惊人的提升(实际上大约是 11400%)。
那么,您可能会问,为什么更轻松?好吧,几乎任何东西都可以转换为流。看看下面的例子:
Dim MyPath As String
Dim MyByteArray() As Byte
Dim MyString As String
Dim MyStream As New Filestream(MyPath)
Dim MyStream As New MemoryStream(MyByteArray)
Dim MyStream As New Memorystream(System.Text.Encoding.Default.GetBytes (MyString))
如您所见,流非常通用。这解决了几乎所有重载的问题!
流也是无尽的,因此规避了解码多部分 yenc 文件的麻烦。由于数据可以写入流的任何位置,因此我无需对部分进行排序即可按顺序写入它们。我打开一个流,将其定位到部分偏移量(从部分标题解析),然后将解码后的数据转储到其中。
其他优化
.NET Framework 的另一个对象实现了惊人的速度提升:StringBuilder 对象。如果您必须连接大字符串,我强烈建议使用此对象。
在我进行的一些测量中,使用此对象进行字符串连接的速度要快 250 倍。
它非常适合此项目,因为编码/解码过程的很大一部分涉及字符串连接。
几句建议
如果速度比呈现更重要,请不要使用事件声明编码器/解码器。如果您处理进度事件,解码速度会明显变慢。
在编码大文件(大于 10MB)时,请勿将其编码到内存中。使用编码到文件的重载。另外,不要过度依赖垃圾回收器。始终销毁变量;这始终是一个好原则。
如果您要尝试对编码/解码过程进行计时,请务必禁用任何防病毒软件。原因是写入的文件流在 AV 完成文件病毒检查之前不会关闭。对于小文件,这可能不会对结果产生明显影响,但对于大文件,这可能会造成很大的麻烦……
最后,如果您正在编码非常大的文件,并且幸运地拥有两个硬盘,请务必从较慢的硬盘读取,并写入较快的硬盘。HDD 写入始终比读取慢,并且 HDD 不能同时进行读写(因此,如果您从同一个 HDD 读取和写入,它会定位其磁头,读取一部分数据,重新定位磁头,写入另一部分数据,依此类推)。
致谢
文档由 VB.Doc 编译成 XML,这是一个免费的 VB.NET 编程语言文档系统。帮助文件由 NDoc 生成。
历史
- 2004 年 3 月 16 日 - 初始发布。
- 2004 年 6 月 4 日 - 对代码进行了彻底重写。在某些情况下,速度提高了 11400%!
反馈和改进
欢迎在下面的论坛中发布问题、改进建议或遇到的问题。我会留意它们并尽可能提供帮助。如果您有任何改进或优化,请发布出来,以便大家都能从中受益。我将对其进行审查,并在注明出处的情况下将其添加到项目中。