关于字节数组写入在 Java 中的工作方式的困惑

Confusion around how byte array writing works in Java

假设我有一大组字符串,我想尽可能高效地写入文件。我不在乎它是不是人类可读的。

我首先想到的是将字符串作为原始字节写入二进制文件。我尝试使用 DataOutputStream 并编写字节数组。但是,当我打开我的文件时,它是可读的。

这是如何运作的?它实际上是在后台编写二进制文件并且只有我的文本编辑器使其可读吗?

这是最有效的方法吗? 我会将其用于性能至关重要的项目,因此我正在寻找写入文件的最快方法(不需要人类可读)。

提前致谢。

文件只是一袋字节。他们总是,即使是 txt 文件。

字符并不真正存在,因为计算机不知道它们是什么,不是真的存在。他们只知道数字。

那么,它是如何工作的?

欢迎来到文本编码的精彩世界。

例如,存储字符串“Hello!”的工作在文件中需要先将Hello!的概念转换为字节,然后将这些字节写入文件。

为了,您首先需要查找table;将字符转换为数字的一种。然后,我们必须将这些数字转换为字节,然后我们才能将它们保存到文件中。

常见的编码是US-ASCII。 US-ASCII 在其映射中仅包含 94 个字符。 26 个大小写形式的英文字母、所有数字、一些有用的符号,例如 !@#$%^&*( 和 space。而已。 US-ASCII 根本没有 'mapping' 例如é 或 ☃ 甚至 .

所有这些字符都映射到 32 到 126 之间的数字,因此要将其放入文本文件中,只需写入该数字即可,因为字节可以表示 0 到 255 之间的任何内容,因此 'just fits' (实际上高位一直为0)

但是,现在是 2021 年,我们有表情符号,而且我们不久前发现,事实证明,有些语言不是英语,太棒了。

所以,常用的table就是unicode table。这 table 表示超过 94 个字符。 Nono,这个 table 在其 table 中有多达 143859 个字符,在撰写本文时。神圣的蝙蝠侠,那是一吨。

显然,这143,859个字形映射到的数字至少必须在0到143,859之间(实际上是一个更大的数字范围;为了方便和为将来的更新留出空间)。

您可以声明每个数字都是一个整数(在 0 和 2^31 之间 - 总共 4 个字节),并将每个字符存储为一个整数(因此,Hello! 会变成磁盘上的一个文件那是 24 个字节大)。

但是,更常见的编码是 UTF-8。 UTF-8 具有 属性 它存储与 ASCII 兼容的字符,好吧,ASCII,因为这 94 个数字在 unicode 中具有与在 ASCII 中相同的 'number translation',AND UTF-8 将这些数字存储为该字节。 UTF-8 将每个字符存储在 1、2、3 或 4 个字节中,具体取决于它是什么字符。这是一个 'variable length encoding scheme'.

您可以查找 UTF_8 例如维基百科,如果你想知道这笔交易。

对于英文文本,UTF-8 非常高效,不比 ascii 差(所以没有理由使用 ascii)。您可以在 java 中轻松完成此操作:

// Path, Files etc are from java.nio.file

Path p = Paths.get("mytextfile.txt");
Files.writeString(p, "Hello!");

这就是你所需要的; Files API 默认为 UTF_8(请注意,旧的且大部分已过时的 API,例如 FileWriter 不会,您应该始终指定字符集为那些编码!或者更好的是,不要使用 em 而是使用 java.nio.file)。

请注意,您可以在其中塞入一个 unicode 雪人甚至表情符号,它会保存得很好。

没有 'binary' 变体。文件 字节。如果您在文本编辑器或 运行 cat thatfile.txt 中打开它,您猜怎么着? cat 或您的编辑器正在读取字节,在黑暗中疯狂刺探它可能是什么编码,查找字符 table 中的每个解码值,然后将工作外包给字体渲染引擎再次显示字符。这只是编辑器给你展示字节文件的好处:

72、101、108、108、111、33

as Hello! 因为这样更容易阅读。用十六进制编辑器打开 'text file',你会看到它包含我显示的数字序列(嗯,十六进制,这也只是为了方便渲染)。

不过,如果您想存储它 'efficiently',答案很简单:使用压缩算法。您可以通过例如抛出该数据new ZipOutputStream 或使用更花哨的压缩器:

Path p = Paths.get("file.txt.gz");
try (OutputStream out = Files.newOutputStream(p);
  ZipOutputStream zip = new ZipOutputStream(out)) {

String shakespeare = "type the complete works of shakespeare here";
zip.write(shakespeare.getBytes(StandardCharsets.UTF_8);
}

您会发现 file.txt.gz 的字节数比莎士比亚作品的总字符数少得多。瞧。效率。

您可以随意使用您的压缩算法;有许多。有些针对特定目的进行了优化,大多数落在 'speed of compression' 和 'efficiency of compression' 之间的折衷线上。许多是可配置的(以 运行 更长的时间与快速压缩为代价更好地压缩,但它不会那么有效)。 java 内置了一些基本的压缩算法,对于更高级的压缩算法,好吧,有一些具有纯粹的 java 实现,您可以使用。