在java中将String拆分为1024字节的块的有效方法是什么?如果存在多个块,则需要在所有后续块中重复头(固定大小的字符串).
你有两种方式,快速和记忆保守的方式.但首先,您需要知道String中的字符.ASCII?是否有变音符号(128到255之间的字符)或甚至Unicode(s.getChar()返回> 256).根据不同,您需要使用不同的编码.如果您有二进制数据,请尝试"iso-8859-1",因为它将保留字符串中的数据.如果您有Unicode,请尝试"utf-8".我假设二进制数据:
String encoding = "iso-8859-1";
最快的方法:
ByteArrayInputStream in = new ByteArrayInputStream (string.getBytes(encoding));
请注意,String是Unicode,因此每个字符都需要两个字节.您必须指定编码(不要依赖于"平台默认".这只会导致以后的痛苦).
现在你可以用1024块来读它
byte[] buffer = new byte[1024]; int len; while ((len = in.read(buffer)) > 0) { ... }
这需要大约三倍于原始字符串的RAM.
更保守的内存方式是编写一个转换器,它接受一个StringReader和一个OutputStreamWriter(包装一个ByteArrayOutputStream).将字节从阅读器复制到编写器,直到底层缓冲区包含一个数据块:
如果是这样,将数据复制到实际输出(在标题前面),将附加字节(Unicode->字节转换可能生成)复制到临时缓冲区,调用buffer.reset()并将临时缓冲区写入缓冲.
代码看起来像这样(未经测试):
StringReader r = new StringReader (string); ByteArrayOutputStream buffer = new ByteArrayOutputStream (1024*2); // Twice as large as necessary OutputStreamWriter w = new OutputStreamWriter (buffer, encoding); char[] cbuf = new char[100]; byte[] tempBuf; int len; while ((len = r.read(cbuf, 0, cbuf.length)) > 0) { w.write(cbuf, 0, len); w.flush(); if (buffer.size()) >= 1024) { tempBuf = buffer.toByteArray(); ... ready to process one chunk ... buffer.reset(); if (tempBuf.length > 1024) { buffer.write(tempBuf, 1024, tempBuf.length - 1024); } } } ... check if some data is left in buffer and process that, too ...
这只需要几千字节的RAM.
[编辑]评论中对字符串中的二进制数据进行了长时间的讨论.首先,将二进制数据放入String中是完全安全的,只要在创建它并将其存储在某处时要小心.要创建这样的String,请使用byte []数组并:
String safe = new String (array, "iso-8859-1");
在Java中,ISO-8859-1(又名ISO-Latin1)是1:1映射.这意味着不会以任何方式解释数组中的字节.现在你可以在数据上使用substring()等,或者用索引搜索它,在其上运行regexp等等.例如,找到一个0字节的位置:
int pos = safe.indexOf('\u0000');
如果您不知道数据的编码并希望在某些编解码器混淆之前查看它,这将特别有用.
要在某处写入数据,反向操作是:
byte [] data = safe.getBytes("iso-8859-1");
切勿使用默认方法new String(array)
或String.getBytes()
!有一天,您的代码将在不同的平台上执行,它将会中断.
现在字符串中的字符> 255的问题.如果您使用此方法,您的字符串中将不会有任何此类字符.也就是说,如果由于某种原因有任何原因,那么getBytes()会抛出异常,因为无法在ISO-Latin1中表达所有Unicode字符,所以在代码不会无声地失败的意义上你是安全的.
有些人可能认为这不够安全,你不应该混合使用字节和字符串.在这个时代,我们没有那么奢侈.许多数据没有明确的编码信息(例如,文件没有"编码"属性,就像它们具有访问权限或名称一样).XML是具有显式编码信息的少数格式之一,并且有像Emacs或jEdit这样的编辑器使用注释来指定这些重要信息.这意味着,在处理字节流时,您必须始终知道它们是哪种编码.截至目前,无论数据来自何处,都无法编写始终有效的代码.
即使使用XML,您也必须将文件的标题作为字节读取,以确定编码,然后才能解码.
重要的是坐下来找出用于生成必须处理的数据流的编码.如果你这样做,你很好,如果你不这样做,你就注定要失败.混淆源于这样的事实:大多数人不知道相同的字节可能意味着不同的东西取决于编码或甚至不止一个编码.此外,如果Sun没有引入"平台默认编码"的概念,它会有所帮助.
初学者的重点:
有多个编码(charset).
比英语使用的字符多.甚至还有几组数字(ASCII,全宽,阿拉伯语 - 印度语,孟加拉语).
您必须知道使用哪种编码来生成您正在处理的数据.
您必须知道应该使用哪种编码来编写正在处理的数据.
您必须知道指定此编码信息的正确方法,以便下一个程序可以解码您的输出(XML标头,HTML元标记,特殊编码注释等).
ASCII的日子结束了.
字符串和字节是两个完全不同的东西,所以想要将一个字符串拆分成字节与想要将一个绘画分成几个节目一样毫无意义.
你真的想做什么?
要在字符串和字节之间进行转换,您需要指定一个可以编码String中所有字符的编码.根据编码和字符,它们中的一些可能跨越多个字节.
您可以将String拆分为1024个字符的块,并将其编码为字节,但每个块可能超过1024个字节.
或者您可以将原始字符串编码为字节,然后将它们拆分为1024个块,但是您必须确保将它们作为字节附加,然后再将整体解码为字符串,否则您可能会在分割点处出现乱码.一个字符跨越1个字节以上.
如果你担心String可能很长时间使用内存,你应该使用流(java.io包)来进行en /解码和拆分,以避免将数据作为副本多次保存在内存中.理想情况下,您应该避免将原始字符串整合在一起,而是使用流从任何地方以小块的形式读取它.