编码和空终止字符串
Encoding and null terminated strings
编辑:我想出了一个解决方案,这里是为任何可能需要它的人准备的。如果发现错误或添加其他改进,它可能会在将来更新。最后更新于 2015 年 7 月 18 日。
/// <summary>
/// Decodes a string from the specified bytes in the specified encoding.
/// </summary>
/// <param name="Length">Specify -1 to read until null, otherwise, specify the amount of bytes that make up the string.</param>
public static string GetString(byte[] Source, int Offset, int Length, Encoding Encoding)
{
if (Length == 0) return string.Empty;
var sb = new StringBuilder();
if (Length <= -1)
{
using (var sr = new StreamReader(new MemoryStream(Source, Offset, Source.Length - Offset), Encoding, false))
{
int ch;
while (true)
{
ch = sr.Read();
if (ch <= 0) break;
sb.Append((char)ch);
}
if (ch == -1) throw new Exception("End of stream reached; null terminator not found.");
return sb.ToString();
}
}
else return Encoding.GetString(Source, Offset, Length);
}
我正在升级我的应用程序的内部 string/Encoding 代码,但我 运行 遇到了一些实施问题。
基本上,我想做一个简单的方法,ReadNullTerminatedString。一开始做起来并不难。我使用 Encoding.IsSingleByte 确定单个字符的长度,读取字节,检查 0,并根据结果停止 reading/continue。
这就是它变得棘手的地方。 UTF8 具有可变长度编码。 Encoding.IsSingleByte returns false,但这并不总是正确的,因为它是一个可变编码,一个字符可以是 1 个字节,所以我基于 Encoding.IsSingleByte 的实现不适用于 UTF8。
那时我不确定该方法是否可以更正,所以我有了另一个想法。只需对字节使用编码的 GetString 方法,使用字符串的最大长度作为计数参数,然后 trim 返回字符串中的零。
这也有一个警告。我必须考虑我的托管应用程序将与从非托管代码返回的字节数组交互的情况,当然会有一个空终止符,但它后面可能有额外的垃圾字符。
例如:
"blah[=29=][=29=]\oldstring"
ReadNullTerminatedString 在这种情况下将是理想的解决方案,但目前如果我希望它支持 UTF8 则不能。第二种解决方案也不起作用 - 它会 trim 0,但垃圾将保留。
对于 C# 的优雅解决方案有什么想法吗?
您最好的解决方案是使用 TextReader
:
的实现
StreamReader
如果您正在从流中读取
StringReader
如果您正在读取字符串
有了这个,你可以读取你的源字节流,用你喜欢的任何编码,每个 "character" 都会作为 int
:
int ch = reader.Read();
在内部,魔术是通过 C# Decoder
class(来自您的编码)完成的:
var decoder = Encoding.UTF7.GetDecoder();
Decoder
class 需要一个短数组缓冲区。幸运的是 StreamReader
知道如何保持缓冲区填充并且一切正常。
伪代码
未经尝试,未经测试,只是碰巧看起来像 C#:
String ReadNullTerminatedString(Stream stm, Encoding encoding)
{
StringBuilder sb = new StringBuilder();
TextReader rdr = new StreamReader(stm, encoding);
int ch = rdr.Read();
while (ch > 0) //returns -1 when we've hit the end, and 0 is null
{
sb.AppendChar(Char(ch));
int ch = rdr.Read();
}
return sb.ToString();
}
Note: Any code released into public domain. No attribution required.
编辑:我想出了一个解决方案,这里是为任何可能需要它的人准备的。如果发现错误或添加其他改进,它可能会在将来更新。最后更新于 2015 年 7 月 18 日。
/// <summary>
/// Decodes a string from the specified bytes in the specified encoding.
/// </summary>
/// <param name="Length">Specify -1 to read until null, otherwise, specify the amount of bytes that make up the string.</param>
public static string GetString(byte[] Source, int Offset, int Length, Encoding Encoding)
{
if (Length == 0) return string.Empty;
var sb = new StringBuilder();
if (Length <= -1)
{
using (var sr = new StreamReader(new MemoryStream(Source, Offset, Source.Length - Offset), Encoding, false))
{
int ch;
while (true)
{
ch = sr.Read();
if (ch <= 0) break;
sb.Append((char)ch);
}
if (ch == -1) throw new Exception("End of stream reached; null terminator not found.");
return sb.ToString();
}
}
else return Encoding.GetString(Source, Offset, Length);
}
我正在升级我的应用程序的内部 string/Encoding 代码,但我 运行 遇到了一些实施问题。
基本上,我想做一个简单的方法,ReadNullTerminatedString。一开始做起来并不难。我使用 Encoding.IsSingleByte 确定单个字符的长度,读取字节,检查 0,并根据结果停止 reading/continue。
这就是它变得棘手的地方。 UTF8 具有可变长度编码。 Encoding.IsSingleByte returns false,但这并不总是正确的,因为它是一个可变编码,一个字符可以是 1 个字节,所以我基于 Encoding.IsSingleByte 的实现不适用于 UTF8。
那时我不确定该方法是否可以更正,所以我有了另一个想法。只需对字节使用编码的 GetString 方法,使用字符串的最大长度作为计数参数,然后 trim 返回字符串中的零。
这也有一个警告。我必须考虑我的托管应用程序将与从非托管代码返回的字节数组交互的情况,当然会有一个空终止符,但它后面可能有额外的垃圾字符。 例如: "blah[=29=][=29=]\oldstring"
ReadNullTerminatedString 在这种情况下将是理想的解决方案,但目前如果我希望它支持 UTF8 则不能。第二种解决方案也不起作用 - 它会 trim 0,但垃圾将保留。
对于 C# 的优雅解决方案有什么想法吗?
您最好的解决方案是使用 TextReader
:
StreamReader
如果您正在从流中读取StringReader
如果您正在读取字符串
有了这个,你可以读取你的源字节流,用你喜欢的任何编码,每个 "character" 都会作为 int
:
int ch = reader.Read();
在内部,魔术是通过 C# Decoder
class(来自您的编码)完成的:
var decoder = Encoding.UTF7.GetDecoder();
Decoder
class 需要一个短数组缓冲区。幸运的是 StreamReader
知道如何保持缓冲区填充并且一切正常。
伪代码
未经尝试,未经测试,只是碰巧看起来像 C#:
String ReadNullTerminatedString(Stream stm, Encoding encoding)
{
StringBuilder sb = new StringBuilder();
TextReader rdr = new StreamReader(stm, encoding);
int ch = rdr.Read();
while (ch > 0) //returns -1 when we've hit the end, and 0 is null
{
sb.AppendChar(Char(ch));
int ch = rdr.Read();
}
return sb.ToString();
}
Note: Any code released into public domain. No attribution required.