繁体   English   中英

BinaryWriter编写有趣的角色

[英]BinaryWriter writes funny characters

下面是代码:

using (FileStream fs = File.Create("data.txt"))
using (BinaryWriter bw = new BinaryWriter(fs))
{
   int num = 2019;
   bw.Write(num);
}

当我使用编辑器打开data.txt时,我只会看到一个有趣的角色。 所以我的问题是:

Q1-这是因为我的编辑器的编码是UTF-8,与BinaryWriter格式不兼容吗? 我应该使用哪种编码方案才能在文本文件中看到act 2019?

问题2-与其他流适配器(例如StreamWriter)相比,BinaryWriter的实际用途是什么? 对我来说BinaryWriter做一些奇怪的事情,例如,您使用BinaryWriter先写一个int,然后写一个字符串...,然后当您通过BinaryReader读取文件时,必须先执行ReadInt32()然后再执行ReadString( ),则无法弄乱序列,如果您执行ReadString(),则会得到一个有趣的字符。 但是谁会“记住”或知道阅读的顺序呢?

好的,让我们从您的代码开始做起(请参阅我添加的注释):

// create a FileStream to data.txt (a file with a .txt extension - not necessarily a text file) 
using (FileStream fs = File.Create("data.txt"))

// wrap the stream in the BinaryWriter class, which assists in writing binary files
using (BinaryWriter bw = new BinaryWriter(fs))
{
   // create a 32-bit integer
   int num = 2019;
   // write a 32-bit integer as 4 bytes
   bw.Write(num);
}

首先要注意的是,您不是在编写文本文件,而是在编写二进制文件。 文件扩展名是一个约定,也许可以告诉我们在文件中应该找到的内容,但这不是福音的真理。 我可以复制一份Chrome.exe并将其重命名为Chrome.txt ,但这并不能使其成为文本文件。

我应该使用哪种编码方案才能在文本文件中看到act 2019?

当我们谈论编码(例如UTF-8)时,我们谈论的是文本编码-如何将文本转换为字节,但是我们不处理代码中的文本,因此没有适用的文本编码格式查看二进制文件。

BinaryWriter相对于其他流适配器(例如StreamWriter)的实际用途是什么?

它使您可以从.NET中的值快速创建二进制格式。 例如,不必手动将int值转换为4个字节,可以调用bw.Write(num); ,同样,您可以使用BinaryReaderbr.ReadInt32()读取数据。

您不能弄乱序列,如果您执行ReadString(),则会得到一个有趣的字符。 但是谁会“记住”或知道阅读的顺序呢?

当我们谈论“文件格式”时,通常是指我们阅读文件时遵循的约定。 我们可以启动应用程序,读取ZIP文件,收听MP3文件或查看位图的原因是因为我们使用的软件已被编写来理解这些二进制格式。

如果以位图为例,则有许多描述文件格式的文档。 快速的Google搜索显示了这个这个这个 您可以选择其中任何一个,并使用BinaryWriter创建一个程序来写入图像文件。

现在,如果要创建自己的格式,则可能会同时编写作者和读者,或者至少要在编写读者时查看给作者的代码(除非您有遵循的规范,您可以使用哪种情况)。

但是我没有得到的是,我插入的int显示为一个有趣的字符,我插入的字符串实际上是可读的,那么为什么字符串是可读的却不是int呢?

调用Write(string) ,实际上是在写两件事:有关字符串长度的信息,然后再编写字符串本身。 为此, BinaryWriter必须将字符串转换为字节,这将在后台为您完成。 您可以在此处在docs中阅读有关内容。

那么,为什么您可以读取文件中的字符串? 好吧,这是因为这里使用的文本编码与编写文本文件所使用的编码相同。 您的文本编辑器将尽力而为地呈现整个文件的内容。 如果将任何类型的二进制文件(例如Chrome.exe )拖动到文本编辑器中,都可以看到。

那么如何查看文件内容? 好了,您可以使用十六进制编辑器 十六进制编辑器允许您查看和编辑二进制文件。 十六进制编辑器通常会在一侧将您的文件显示为十六进制,而在另一侧尝试将其呈现为文本。

因此,假设您的代码是这样的:

using (FileStream fs = File.Create("data.txt"))
using (BinaryWriter bw = new BinaryWriter(fs))
{
   int num = 2019;
   bw.Write(num);
   bw.Write("hello");
}

如果在十六进制编辑器中打开它,则会看到以下内容。 请注意,十六进制值之间的空格仅是为了使其更易于阅读,而不表示文件中的任何内容:

E3 07 00 00 05 68 65 6C 6C 6F

这里分为三个部分:

E3 07 00 00    - the hexadecimal expression of little endian 2019
05             - indicating that the string is 5 _bytes_ long
68 65 6C 6C 6F - the hexadecimal representations of each character of the string "hello"

您可以在此处阅读有关字节序的信息 可以将其视为计算机是写数字“从左到右”还是“从右到左”。

因此,查看上面存储的int值,我们可以将其写入big-endian(右侧为1)二进制文件中,如下所示:

<  00   >  <  00   >  <  07   >  <  E3   >
0000 0000  0000 0000  0000 0111  1110 0011

然后我们可以将其计算回2019年,即您的原始值。

请注意,字符串长度信息可以超过一个(根据此答案 )。

这都是文件格式的问题。

使用StreamWriter时,输出将以可读文本显示,这意味着您可以看到编辑器中的内容。 例如,您可能会在布尔值中写"true""false" 。使用二进制编写器时,该值以其二进制表示形式存储,对于布尔值,该值为0或1。 请注意,如果愿意,您可以在文本文件中将"0"写为true。

当要记住里面的内容时,要么使用自我描述的文件格式(例如带标题的csv),要么必须使用标准格式(例如可以在线查找描述的MP3),或者必须同时编写读者和作家,以确保他们匹配(即使使用文本格式)。

例如,通过查看"0,0"您无法确定它的两个布尔值是由逗号分隔还是法语格式的数字0(具有一位精度)。

文件是数字字符串,例如13、59、93。要了解文件的内容,您需要一种格式 -本质上是对内容含义的描述。 要查看文件的字节,可以使用十六进制编辑器(而不是文本编辑器)。

一种这样的格式是文本文件。 请注意,没有一种文本文件格式-正如您已经注意到的,您的文本编辑器允许您选择在解释文本文件时将使用的编码。 如果您选择了错误的编码,则文本将有所不同(尽管大多数英文编码可能不会引起注意,因为在大多数现代编码中,许多字符是相同的)。 编码是将数字65 (实际上存储在文件中)转换为字符'A' 除了编码,还有许多其他复杂性,我将在后面讨论。

您正在使用BinaryWriter 顾名思义,它是为写入二进制文件而不是文本文件而设计的。 如果要编写纯文本文件,请改用StreamWriter 二进制文件通常比文本文件更紧凑,二进制文件设计为由特定应用程序使用,而不是由用户直接读取或修改。 您仍然可以在二进制文件中写入文本-这正是bw.Write("Hello")所做的; 并且由于它使用与文本编辑器相同的编码(默认情况下),因此您实际上在编辑器中看到单词“ Hello”。 请注意,在“ Hello” 之前还有“有趣的字符” 但是对于这么短的字符串,它们是不可见的 (有些可能显示为空格,另一些则显示为控制字符,例如“行尾”或“制表符”;您甚至可以写一个哔声 ,如果您打印出该文件,该哔哔声将被执行)。 它们代表以下字符串的长度,这使您可以快速读取该字符串,并且仅读取该字符串(或者在读取文件时跳过该字符串)。

现在,读写文件需要一定的对称性。 如您所见,如果您将文件写为“先编号,然后是字符串”,则还需要先将其为“先编号,然后是字符串”。 文件是文本文件还是二进制文件都没有关系-例如,假设您要将GPS坐标记录为文件。 如果先写纬度,然后再写经度,则另一个程序(或用户)首先读取文件为经度,将会得到错误的结果。 像这样的简单文件格式取决于顺序,并且完全不容许任何类型的错误-读或写时跳过一行,整个内容变得完全不可读。

但是,当然,这不是设计文件格式的唯一方法(尽管这当然很常见)。 有些格式明确设计为不太严格。 例如,您可以将数据保存在JSON文件中,而不用一组行或逗号分隔的值:

{
  "longitude": 12.365,
  "lattitude": 32.131
}

主要好处是该格式更具描述性,易于阅读(可写)。 您可以一眼看出32.13132.131 应用程序仍然需要了解什么是“纬度”,但是您可以看到这里肯定有进步。 它还对某些更改具有更大的容忍度-例如,阅读器应用程序不必担心某些字段是否丢失(显示的信息不完整,而不是完整的混乱),或者是否添加了新字段。 它不在乎字段的顺序。

这是有代价的。 该文件大得多(一个简单的二进制文件可能不超过8个字节,而示例JSON则约为40个字节左右;如果涉及数组等,则这一点更加明显)。 程序解析起来要困难得多,这可能会使加载文件的速度变慢。 对格式不严格也有其好处和弊端-确保程序正确处理所有可能的输入可能非常困难,尤其是在存在多个不同的读者和作家的情况下。

二进制文件也有等效的文件格式,Protobuf是当今最受欢迎的文件格式之一。 它不具有自我描述性,不能轻易被人阅读,但是它更严格,更节省空间并且读写速度更快。

最后,您需要选择要用于保存内容的格式。 每一种都有其自身的优点和缺点。 有些非常简单,例如仅使用BinaryWriter编写众所周知的序列。 有些支持版本兼容性,因此,较新的应用程序可以读取或写入旧应用程序的文件,反之亦然。 有些文件针对某些用途进行了专门优化,例如可以快速搜索文件的内容或有效地存储图像。 有些主要是为了易于使用而设计的(例如JSON和Protobuf或.NET的BinarySerializer )。

但最后,文件只是一串数字。 您需要规则来解释这些数字才有用。 选择适合您需要的规则。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM