【问题标题】:StringBuilder append byte without formattingStringBuilder 附加字节而不格式化
【发布时间】:2026-02-06 10:05:01
【问题描述】:
DateTime todayDateTime = DateTime.Now;
StringBuilder todayDateTimeSB = new StringBuilder("0");
todayDateTimeSB.Append(todayDateTime.ToString("MMddyyyy"));
long todayDateTimeLongValue = Convert.ToInt64(todayDateTimeSB.ToString());
// convert to byte array packed decimal
byte[] packedDecValue = ToComp3UsingStrings(todayDateTimeLongValue); 
// append each byte to the string builder
foreach (byte b in packedDecValue)
{
    sb.Append(b); // bytes 56-60
}
sb.Append(' ', 37);

上面的代码获取当前日期时间,将其格式化为一个长值并将其传递给将其转换为压缩十进制格式的方法。我知道上述方法有效,因为当我单步执行代码时,字节数组对我期望的所有字节都有正确的十六进制值。

但是上面是我遇到问题的代码,特别是我研究并发现字符串生成器.Append(byte) 实际上为那个字节做了一个ToString()。当它将字节添加到字符串时,它会改变字节的值。问题是我如何告诉StringBuilder 按原样获取“字节”并将其存储在内存中而不格式化/更改值。我知道还有一个.AppendFormat() 有几个重载,它们使用IFormatProvider 提供很多关于如何格式化事物的选项,但我看不到任何方法告诉它不要格式化/更改/更改数据的价值。

【问题讨论】:

  • The question is how do I tell the StringBuilder to take the 'byte' as is and store it in memory without formatting/altering the value. 好吧,你真的不能,因为 StringBuilder 代表一个可变的字符序列,但 一个字节不是一个字符。如果您知道编码(可能是 ASCII),则可以将字节转换为字符。

标签: c# stringbuilder iformatprovider


【解决方案1】:

您可以将字节转换为字符:

sb.Append((char)b);

您也可以使用ASCIIEncoding 一次转换所有字节:

string s = Encoding.ASCII.GetString(packedDecValue);
sb.Append(s);

【讨论】:

  • 非常感谢!这是一个真正的救生员。这也应该防止转换 ton BSTR 出现任何问题,因为它存储为 ASCII 值。我也可以让转换实用程序进行转换,但我喜欢你转换为字符串的解决方案。
  • 我使用了字符串 s = Encoding.ASCII.GetString(packedDecValue)。这似乎可行,但在某些情况下,当我们将其添加到字符串生成器时,它仍然会使字符串出现乱码。从外观上看,当我们在扩展的 ascii 集中获得一个 ASCII 值时,特别是我们遇到问题的值是 {0x01,0x99,0x0c}。它作为一个字符串从编码中出来,但是当它被添加到字符串构建器时,它会被转换为其他东西。 Encoding.ASCII.GetString(packedDecValue) 的值是“?\f”,可能是由于 \?
  • 从上面开始,我刚刚投票的下一个响应解决了这一点,因为 0x99 超出了 0x00-0x7f 范围?表示它正在丢弃数据。因此,对于打包的 dec,此解决方案将不起作用。
【解决方案2】:

如前所述,在 Unicode 世界中,字节(八位字节)不是字符。 CLR 在内部使用 Unicode 字符,并在内部以 UTF-16 编码表示它们。 StringBuilder 构建一个 UTF-16 编码的 Unicode 字符串。

然而,一旦你有了那个 UTF-16 字符串,你就可以重新编码它,比如使用 UTF-8 或 ASCIIEncoding。但是,在这两种情况下,代码点 0x0080 和更高版本都不会保持原样。

UTF-8 使用 2 个八位字节表示代码点 0x0080–0x07FF; 3 个八位字节用于代码点 0x0800–0xFFFF 等等。 http://en.wikipedia.org/wiki/UTF-8#Description

ASCII 编码更糟糕:根据the documentation,0x0000–0x007F 之外的代码点被简单地丢弃:

如果您使用 Encoding.ASCII 属性或 ASCIIEncoding 构造函数,超出该范围的字符被替换为 执行编码操作之前的问号 (?)。

如果您需要毫发无损地发送八位字节流,最好使用 System.IO.MemoryStream 包裹在 StreamReaderStreamWriter 中。

然后您可以通过GetBuffer() 方法或ToArray() 方法访问MemoryStream 的后备存储。 GetBuffer() 为您提供对实际后备存储的引用。但是它可能包含已分配但未使用的字节——您需要检查流的LengthCapacityToArray() 分配一个新数组并将实际流内容复制到其中,因此您收到的数组引用是正确的长度。

【讨论】:

  • 感谢您提供背景信息。很高兴知道,出于我的目的,ascii 编码应该可以正常工作,因为我所有的值都应该在 ascii 编码范围内。但是,如果我做的不是将数字转换为压缩十进制,这可能是一个问题...... IE 试图存储具有可能较大值的原始二进制数据。
  • 看来我遇到了 ASCII 码点。我仍然可以使用 UTF-8 进行编码,还是我需要完全放弃使用 stringbuilder 并使用 streamreader?查看对当前最高更新的评论。
  • 有没有办法获取这个内存流/流读取器并将流转换为 bstr?
  • 更新,事实证明你不能使用 streamwriter,因为它使用 textwriter,所以当你尝试将字节数组复制到使用它的流时,你会遇到同样的问题。所以,我只是使用基本内存流,将“字符串”部分转换为字节数组,并使用内存流在字节级别做所有事情......仍然需要将最终的字节数组转换为 BSTR,但已经取得了进展。