在java中将String拆分为1024字节的块的有效方法是什么? 如果有多个块,则需要在所有后续块中重复标题(固定大小的字符串)。
答案 0 :(得分:8)
你有两种方式,快速和记忆保守的方式。但首先,您需要知道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(a.k.a 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没有引入“平台默认编码”的概念,它会有所帮助。
初学者的重点:
ASCII的日子结束了。
答案 1 :(得分:5)
字符串和字节是两个完全不同的东西,所以想要将一个字符串拆分成字节就像想要将一个绘画分成几个节目一样毫无意义。
你真正想做的是什么?
要在字符串和字节之间进行转换,您需要指定一个可以编码String中所有字符的编码。根据编码和字符,其中一些可能跨越多个字节。
您可以将String拆分为1024个字符的块,并将其编码为字节,但每个块可能超过1024个字节。
或者您可以将原始字符串编码为字节,然后将它们拆分为1024个块,但是您必须确保将它们作为字节附加,然后再将整体解码为字符串,否则您可能会在字符串中出现乱码。当一个字符超过1个字节时分割点。
如果你担心字符串可能很长时使用内存,你应该使用流(java.io包)来进行解码和拆分,以避免将数据保存在内存中几次副本。理想情况下,您应该避免将原始字符串整合在一起,而是使用流从任何位置以小块的形式读取它。
答案 2 :(得分:3)
我知道我迟到了,不过我自己在寻找解决方案然后找到答案作为最佳答案:
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
答案 3 :(得分:0)
我正在为自己尝试此操作,我需要将一个巨大的String(将近10 MB)除以1 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
答案 4 :(得分:0)
是的,以上大多数(如果不是全部)肯定可以工作。
或者您可以签出this项目来完成该任务;它不仅能够对字符串进行分块,而且还能对字节数组,输入流和文件进行分块。
它有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