将 Java 字符串拆分为 1024 字节的块
题
在java中将字符串分割成1024字节的块的有效方法是什么?如果有多个块,则需要在所有后续块中重复标头(固定大小字符串)。
解决方案
的字符串和字节是两个完全不同的东西,所以想要分割字符串转换成字节是作为想要画分成节作为无意义的。
它是什么,你真正想要做什么?
要字符串和字节之间转换,需要指定可编码在字符串中的所有字符的编码。根据不同的编码和字符,它们中的一些可以跨越多于一个字节。
您可以分割字符串成的1024个字符的块和编码的那些字节,但然后每个组块可以是多于1024个字节。
或者你可以编码原始字符串转换成字节,然后将其拆分成1024块,但你必须确保整个重新解码为一个字符串之前追加他们为字节,或者你可能会在乱码当一个字符跨越超过1个字节的分割点。
如果您担心内存使用情况时,字符串可能会很长,你应该使用流(java.io包中)向EN /解码和分离,以避免多次将数据保持在内存中副本。理想情况下,你应该避免不惜一切在一块原始字符串,而是使用流无论你怎么得来的小块读它。
其他提示
有两种方法,快速方法和内存保守方法。但首先,您需要知道字符串中有哪些字符。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));
请注意,字符串是 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。
[编辑] 评论中对字符串中的二进制数据进行了长时间的讨论。首先,只要您在创建二进制数据并将其存储在某个地方时小心谨慎,将二进制数据放入字符串中是完全安全的。要创建这样的字符串,请使用 byte[] 数组并:
String safe = new String (array, "iso-8859-1");
在 Java 中,ISO-8859-1(又名 ISO-Latin1)是 1:1 映射。这意味着数组中的字节不会以任何方式解释。现在您可以在数据上使用 substring() 等,或者使用索引进行搜索,对其运行正则表达式等。例如,查找 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 没有引入“平台默认编码”的概念,也会有所帮助。
对于初学者来说重要的一点:
- 有不止一种编码(字符集)。
- 字符数量比英语使用的字符还要多。甚至还有好几个 数字组 (ASCII、全角、阿拉伯-印度语、孟加拉语)。
- 您必须知道使用哪种编码来生成您正在处理的数据。
- 您必须知道应该使用哪种编码来写入正在处理的数据。
- 您必须知道指定此编码信息的正确方法,以便下一个程序可以解码您的输出(XML 标头、HTML 元标记、特殊编码注释等)。
ASCII 的时代已经结束了。
我知道我迟到了,但我一直在寻找一个解决方案我自己,然后发现我的回答是最好的答案:
private static String chunk_split(String original, int length, String separator) throws IOException {
ByteArrayInputStream bis = new ByteArrayInputStream(original.getBytes());
int n = 0;
byte[] buffer = new byte[length];
String result = "";
while ((n = bis.read(buffer)) > 0) {
for (byte b : buffer) {
result += (char) b;
}
Arrays.fill(buffer, (byte) 0);
result += separator;
}
return result;
}
示例强>:
public static void main(String[] args) throws IOException{
String original = "abcdefghijklmnopqrstuvwxyz";
System.out.println(chunk_split(original,5,"\n"));
}
<强>输出强>:
abced
fghij
klmno
pqrst
uvwxy
z
我是想这对于我自己,我需要1 MB块巨大的字符串(近10 MB)。 这有助于在大块时间最小量的数据。 (不到一秒)。
private static ArrayList<String> chunkLogMessage(String logMessage) throws Exception {
ArrayList<String> messages = new ArrayList<>();
if(logMessage.getBytes().length > CHUNK_SIZE) {
Log.e("chunk_started", System.currentTimeMillis()+"");
byte[] buffer = new byte[CHUNK_SIZE];
int start = 0, end = buffer.length;
long remaining = logMessage.getBytes().length;
ByteArrayInputStream inputStream = new ByteArrayInputStream(logMessage.getBytes());
while ((inputStream.read(buffer, start, end)) != -1){
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
outputStream.write(buffer, start, end);
messages.add(outputStream.toString("UTF-8"));
remaining = remaining - end;
if(remaining <= end){
end = (int) remaining;
}
}
Log.e("chunk_ended", System.currentTimeMillis()+"");
return messages;
}
messages.add(logMessage);
return messages;
}
logcat的:
22:08:00.262 3382-3425/com.sample.app E/chunk_started: 1533910080261
22:08:01.228 3382-3425/com.sample.app E/chunk_ended: 1533910081228
22:08:02.468 3382-3425/com.sample.app E/chunk_started: 1533910082468
22:08:03.478 3382-3425/com.sample.app E/chunk_ended: 1533910083478
22:09:19.801 3382-3382/com.sample.app E/chunk_started: 1533910159801
22:09:20.662 3382-3382/com.sample.app E/chunk_ended: 1533910160662
是的,如果不是所有上面肯定会工作。
或者你可以检查出这个项目,这正是这么做的;只有它能够块不只是字符串,但也字节数组,inputstreams和文件。
它有2类:DataChunker
和StringChunker
DataChunker chunker = new DataChunker(8192, blob) {
@Override
public void chunkFound(byte[] foundChunk, int bytesProcessed) {
//process chunk here
}
@Override
public void chunksExhausted(int bytesProcessed) {
//called when all the blocks have been exhausted
}
};
String blob = "Experience is wasted if history does not repeat itself...Gbemiro Jiboye";
final StringBuilder builder = new StringBuilder();
StringChunker chunker = new StringChunker(4, blob) {
@Override
public void chunkFound(String foundChunk, int bytesProcessed) {
builder.append(foundChunk);
System.out.println("Found: "+foundChunk+", bytesProcessed: "+bytesProcessed+" bytes");
}
@Override
public void chunksExhausted(int bytesProcessed) {
System.out.println("Processed all of: "+bytesProcessed+" bytes. Rebuilt string is: "+builder.toString());
}
};
在构造blob
构造函数的Datachunker's
要么是一个字节数组,一个File
或InputStream