【问题标题】:How can I save a String Byte without losing information?如何在不丢失信息的情况下保存字符串字节?
【发布时间】:2019-11-05 16:51:26
【问题描述】:

我正在开发一个 JPEG 解码器(我处于 Huffman 阶段)并且我想将 BinaryString's 写入一个文件。 例如,假设我们有这个:

String huff = "00010010100010101000100100";

我尝试将其转换为整数,将其除以 8 并将其保存为整数表示,因为我无法写入位:

huff.split("(?<=\\G.{8})"))
int val = Integer.parseInt(str, 2);
out.write(val); //writes to a FileOutputStream

问题在于,在我的示例中,如果我尝试保存 "00010010",它会将其转换为 18 (10010),并且 我需要0。

最后,当我读到:

int enter;
String code = "";
    while((enter =in.read())!=-1) {
            code+=Integer.toBinaryString(enter);
        }

我明白了:

Code = 10010

代替:

Code = 00010010

我也尝试将其转换为 bitset,然后转换为 Byte[],但我遇到了同样的问题。

【问题讨论】:

  • 这不会“丢失”信息,因为您知道丢失的位全为零。你需要的只是用零填充结果。
  • @kaya3 好的,但是当我尝试读取文件时,我怎么知道我得到了一些零并且我需要左填充结果?
  • 总是需要左填充结果。 left-pad 表示从左边填充到特定长度;你想要的长度是 8,所以如果字符串的长度已经是 8,left-padding 不会改变它。
  • @kaya3 我已经编辑了我的问题,因为我认为 left-pad 不是解决方案:(
  • 用这样的字符串实现霍夫曼编码对性能来说绝对是一场灾难。不仅慢,而且是灾难级的慢。

标签: java string int byte bitset


【解决方案1】:

你的例子是你有字符串"10010",你想要字符串"00010010"。也就是说,您需要用零填充此字符串。请注意,由于您在循环中加入了多次调用 Integer.toBinaryString 的结果,因此在连接它们之前,您需要在循环内填充这些字符串。

while((enter = in.read()) != -1) {
    String binary = Integer.toBinaryString(enter);
    // left-pad to length 8
    binary = ("00000000" + binary).substring(binary.length());
    code += binary;
}

【讨论】:

  • 工作少:binary = "00000000".substring(binary.length()) + binary;
【解决方案2】:

您可能想查看 UTF-8 算法,因为它完全符合您的要求。它存储大量数据,同时丢弃零、保留相关数据并对其进行编码以占用更少的磁盘空间。

适用于:Java 版本 7+

import java.nio.charset.StandardCharsets;
import java.util.Formatter;

public class UTF8EncodeDecode {

    public static byte[] utf8encode(int codepoint) {
        return new String(new int[]{codepoint}, 0, 1).getBytes(StandardCharsets.UTF_8);
    }

    public static int utf8decode(byte[] bytes) {
        return new String(bytes, StandardCharsets.UTF_8).codePointAt(0);
    }

    public static void main(String[] args) {
        System.out.printf("%-7s %-43s %7s\t%s\t%7s%n",
                "Char", "Name", "Unicode", "UTF-8 encoded", "Decoded");

        for (int codepoint : new int[]{0x0041, 0x00F6, 0x0416, 0x20AC, 0x1D11E}) {
            byte[] encoded = utf8encode(codepoint);
            Formatter formatter = new Formatter();
            for (byte b : encoded) {
                formatter.format("%02X ", b);
            }
            String encodedHex = formatter.toString();
            int decoded = utf8decode(encoded);
            System.out.printf("%-7c %-43s U+%04X\t%-12s\tU+%04X%n",
                    codepoint, Character.getName(codepoint), codepoint, encodedHex, decoded);
        }
    }
}

https://rosettacode.org/wiki/UTF-8_encode_and_decode#Java

UTF-8 是一种可变宽度字符编码,能够使用一到四个 8 位字节对 Unicode 中的所有 1,112,064[nb 1] 个有效代码点进行编码。[nb 2] 该编码由 Unicode 标准定义,并且是最初由 Ken Thompson 和 Rob Pike 设计。[1][2]该名称源自 Unicode(或通用编码字符集)转换格式 – 8 位。[3]

它是为向后兼容 ASCII 而设计的。具有较低数值的代码点往往更频繁地出现,使用较少的字节进行编码。 Unicode 的前 128 个字符与 ASCII 一对一对应,使用与 ASCII 具有相同二进制值的单个字节进行编码,因此有效的 ASCII 文本也是有效的 UTF-8 编码的 Unicode。由于在将非 ASCII 码位编码为 UTF-8 时不会出现 ASCII 字节,因此在大多数以特殊方式解释某些 ASCII 字符的编程和文档语言中使用 UTF-8 是安全的,例如“/”(斜杠)文件名、转义序列中的“\”(反斜杠)和 printf 中的“%”。

https://en.wikipedia.org/wiki/UTF-8

二进制 11110000 10010000 10001101 10001000 在 UTF-8 中变为 F0 90 8D 88。由于您将其存储为文本,因此您从必须存储 32 个字符变为存储 8 个字符。而且由于它是一种众所周知且设计良好的编码,您可以轻松地对其进行反转。所有的数学都是为你完成的。

00010010100010101000100100(或者更确切地说是00000001 0010100 0101010 00100100)的示例转换为*$(我的机器上的两个不可打印字符)。这就是二进制文件的 UTF-8 编码。我错误地使用了另一个网站,该网站使用我输入的十进制数据而不是二进制数据。

https://onlineutf8tools.com/convert-binary-to-utf8

对于 UTF-8 以及它如何应用于答案的一个非常好的解释:

https://hackaday.com/2013/09/27/utf-8-the-most-elegant-hack/

编辑:

我认为这个问题是为了减少存储值所需的字符数量,这是一种编码。 UTF-8 是一种编码。以“非标准”方式使用,OP 可以使用 UTF-8 以更短的格式对其 0 和 1 的字符串进行编码。这就是这个答案的相关性。

如果将字符连接起来,则可以轻松地从 4x 8 位(32 位)变为 8x 8 位(64 位),并编码一个大至 9,223,372,036,854,775,807 的值。

【讨论】:

  • 这与问题无关。问题中的字符串仅包含字符'0''1'
  • @kaya3,UTF-8 是一种将二进制数据压缩成更小的字节块的方法。
  • 不,UTF-8 是一种对文本数据进行编码的方式。二进制数据可以以简单的方式以每字节 8 位进行编码,当然你不能通过想象数据代表文本来做得更好。
  • @kaya3,这是一种将二进制编码为十六进制值的方法,所以是的,这是一种更好的数据存储方法。看看我新的最后一段。
  • 根据你的最后一段,我怀疑你不明白 UTF-8 和十六进制的区别。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-09
  • 2014-06-11
  • 2018-05-25
  • 1970-01-01
  • 2017-05-29
  • 2015-07-08
  • 1970-01-01
相关资源
最近更新 更多