从长度为无符号整数的 ByteBuffer 中读取 UTF-8 字符串

Posted

技术标签:

【中文标题】从长度为无符号整数的 ByteBuffer 中读取 UTF-8 字符串【英文标题】:Reading a UTF-8 String from a ByteBuffer where the length is an unsigned int 【发布时间】:2009-03-08 20:07:47 【问题描述】:

我正在尝试通过 java.nio.ByteBuffer 读取 UTF8 字符串。大小是一个未分割的 int,当然,Java 没有。我已将值读入 long 以便我拥有该值。

我遇到的下一个问题是我无法使用 long 创建一个字节数组,并且将他的 long 转换回 int 会导致它被签名。

我也尝试在缓冲区上使用 limit(),但它同样适用于 int not long。

我正在做的具体事情是从类文件中读取 UTF8 字符串,因此缓冲区中的内容不仅仅是 UTF8 字符串。

关于如何从 ByteBuffer 中读取可能长度为 unsigned int 的 UTF8 字符串的任何想法。

编辑:

Here is an example of the issue.

SourceDebugExtension_attribute 
       u2 attribute_name_index;
       u4 attribute_length;
       u1 debug_extension[attribute_length];
    

attribute_name_index
    The value of the attribute_name_index item must be a valid index into the constant_pool table. The constant_pool entry at that index must be a CONSTANT_Utf8_info structure representing the string "SourceDebugExtension".

attribute_length
    The value of the attribute_length item indicates the length of the attribute, excluding the initial six bytes. The value of the attribute_length item is thus the number of bytes in the debug_extension[] item.

debug_extension[]
    The debug_extension array holds a string, which must be in UTF-8 format. There is no terminating zero byte.

    The string in the debug_extension item will be interpreted as extended debugging information. The content of this string has no semantic effect on the Java Virtual Machine.

因此,从技术角度来看,类文件中可能有一个长度为完整 u4(无符号,4 字节)的字符串。

如果 UTF8 字符串的大小有限制,这些都不是问题(我不是 UTF8 专家,所以可能有这样的限制)。

我可以直接放弃它并接受这样一个现实,即不会有那么长的字符串......

【问题讨论】:

【参考方案1】:

除非您的字节数组超过 2GB(Java int 的最大正值),否则将 long 转换回带符号的 int 不会有问题。

如果您的字节数组长度需要超过 2GB,那么您做错了,尤其是因为这远远超过了 JVM 的默认最大堆大小...

【讨论】:

在编码>1字节/字符的情况下,字节数组当然可以更长。 String 封装的是 char[] 数组,而不是 byte[] 数组。 当然,可以更长。但它永远不会达到 2 GB。 但无论如何,Java 数组(显然也不是 ByteBuffer)不能超过 2 GB。说真的,只要接受这是您的限制,并让您的代码在极不可能的情况下给出错误,即 long 无法干净地转换为 int。 @Alnitak 已售出!我只需要被说服! :-) 谢谢 如果您可以对字符串的一部分一个接一个地做任何您需要做的事情,而不是同时将其全部保存在内存中,那么没有理由失败。【参考方案2】:

签署 int 不会是您的主要问题。假设你有一个长度为 40 亿的字符串。您需要一个至少为 4 GB 的 ByteBuffer,一个至少为 4 GB 的 byte[]。将其转换为字符串时,您需要至少 8 GB(每个字符 2 个字节)和一个 StringBuilder 来构建它。 (至少 8 GB) 你只需要 24 GB 来处理 1 个字符串。即使你有很多内存,你也不会得到很多这种大小的字符串。

另一种方法是将长度视为有符号,如果无符号则视为错误,因为无论如何您都没有足够的内存来处理字符串。即使要处理长度为 20 亿 (2^31-1) 的字符串,您也需要 12 GB 才能以这种方式将其转换为字符串。

【讨论】:

【参考方案3】:

Java 数组使用(Java,即已签名)int 来访问 as per the languge spec,因此不可能有一个长于 Integer.MAX_INT 的 String(由 char 数组支持)

但是,即使是这么多也无法在一个块中处理 - 如果遇到足够大的字符串,它会完全降低性能并使您的程序在大多数机器上因 OutOfMemoryError 而失败。

您应该做的是处理任何大小合理的字符串,一次说几兆。那么你可以处理的大小没有实际限制。

【讨论】:

【参考方案4】:

我猜你可以在 ByteBuffer 之上实现CharSequence。这将允许您防止您的“字符串”出现在堆上,尽管大多数处理字符的实用程序实际上都需要一个字符串。即便如此,实际上 CharSequence 也有一个限制。它期望大小以 int 形式返回。

(理论上,您可以创建一个新版本的 CharSequence,它以 long 形式返回大小,但是在 Java 中没有任何东西可以帮助您处理该 CharSequence。如果您将 subSequence(...) 实现为返回一个普通的 CharSequence。)

【讨论】:

以上是关于从长度为无符号整数的 ByteBuffer 中读取 UTF-8 字符串的主要内容,如果未能解决你的问题,请参考以下文章

将 4 个字节转换为无符号 32 位整数并将其存储在 long

如何将以“\ 0”开头的char *转换为无符号整数? [关闭]

为什么IS_ERR_VALUE将负MAX_ERRNO转换为无符号长整数?

从ByteBuffer中解析整数

将小端序列中的4个字节转换为无符号整数

如何在python中将有符号整数转换为无符号整数