BufferedReader的read()太慢,readLine()不返回换行或回车符。

huangapple go评论28阅读模式
英文:

BufferedReader's read() too slow, readLine() returns no NL or CR

问题

我正在使用BufferedReader读取通过TCP传输的字节流。问题是readLine()会一直读取直到遇到字节10或10和13。它不会返回这些换行符和回车符。我需要所有的字节(因为它们都是数据)。如果只有换行符的话,我可以在每次readLine()之后添加'\n',但问题是我不知道是否有回车符在换行符之后,回车符也会被readLine()移除,所以我也不知道是否应该添加回车符。使用read()可以解决这个问题,但它非常、非常、非常慢。不可用。有没有其他方法可以使用?因为唯一的方法是编辑通过服务器传输的数据以适应这个愚蠢的readLine()算法...

英文:

I am reading bytes coming through TCP with BufferedReader. Problem is readLine() reads until byte 10 or 10 and 13. It does not return those NL and CR. I need all bytes (because they all are data). With just NL it is ok, I can add '\n' after every readLine(), but problem is I dont know if there is carriage return after NL, which would also be removed by readLine(), so I wouldnt know if to add it too. read() solves this problem but it is very, very, very slow. Unusable. Is there a way to use something else? Because the only way to do that is to edit data coming through server to fit this stupid readLine() algorithm...

答案1

得分: 1

如果你的读取速率大约为每分钟70千字节,那么问题可能出在你的操作系统、网络连接,或者你正在读取的服务器上。

重写客户端代码不会解决这个问题。

英文:

If your read rate is ~70kb per minute, then the problem is in your operating system or network connection or the server you are reading from.

Rewriting the client-side code won't fix this problem.

答案2

得分: 0

在现代的Java中,这个问题已经得到解决,但Android使用的库比较古老... 不过我认为Guava库是可用的(而且可能已经默认包含了?) - 尝试这样做:

InputStream in = socket.getInputStream();
String data;
try {
    InputStreamReader reader = new InputStreamReader(in, "UTF-8");
    // 你需要弄清楚使用的编码是什么
    // - 通常是UTF-8或ISO-8859-1。
    // 如果你不指定编码,就会使用平台默认值。你不想要那个。

    data = com.google.common.io.CharStreams.toString(reader);

} finally {
    in.close();
}

System.out.println(data);

关键是不要调用read(),而是调用read(char[]) - 但这是一个有点难以使用的方法(它保证不会在到达流的末尾或至少读取1个字符之前返回;它不一定会填满字符数组。CharStreams.toString方法将以尽可能快的速度读取并读取所有内容。

如果上面的代码仍然导致70kb/s的传输速率,那么这就是实际情况。手机的连接速度慢或服务器速度慢,或者两者之间有一些问题。考虑到你目前正在使用BufferedReader,我相当确信这确实是问题所在(BufferedReader的整个目的是它的read()方法相对较快,而原始流的read()方法往往非常慢)。

注意:这里可能你并不需要一个字符串,而是需要字节。同样的原理:

InputStream in = socket.getInputStream();
byte[] data;
try {
    // 现在不需要担心编码了!

    data = com.google.common.io.ByteStreams.toByteArray(in);
} finally {
    in.close();
}
英文:

On modern java this is a solved problem but android has ancient libraries... but I think guava is usable (and possibly, included by default already?) - try this:

InputStream in = socket.getInputStream();
String data;
try {
    InputStreamReader reader = new InputStreamReader(in, "UTF-8");
    // you're going to have to figure out what the encoding is
    // - usually it's either UTF-8 or ISO-8859-1.
    // if you don't specify it, you get platform default. You don't want that.

    data = com.google.common.io.CharStreams.toString(reader);

} finally {
    in.close();
}

System.out.println(data);

The trick is not to call read(), but to call read(char[]) - but this is a method that is a bit tricky to use (it guarantees that it won't return until end of stream is reached or at least 1 character is read; it does not fill up the char array necessarily. The CharStreams.toString method will read as fast as possible and read it all.

If the above code still results in a 70kb/s transfer rate, well, then that's what it is. The phone's connection is slow or the server is slow, or something in between. Given that you're currently using BufferedReader, I'm pretty sure this is indeed the case (The whole point of BufferedReader is that its read() method is decently fast, vs. raw streams where that tends to be very slow).

NB: It is possible you don't want a string at all here, but bytes instead. Same principle:

InputStream in = socket.getInputStream();
byte[] data;
try {
    // now no need to worry about encoding!

    data = com.google.common.io.ByteStreams.toByteArray(in);
} finally {
    in.close();
}

答案3

得分: 0

看起来你试图读取二进制数据而不是文本数据,所以readLine()不合适。正如Henry在他们的评论中指出的那样,readLine()使用了其中一个读取方法,所以不可能比它们中的任何一个更快。尝试使用一个接受缓冲区(byte[])作为参数的读取操作。

如果这没有解决问题,考虑一下是否是服务器的速度或速度是否受到对read()的调用的限制,而不是在数据被读取后发生的事情。如果你只以你的代码能够处理的速度进行读取,那么你的处理管道的后续阶段可能是问题所在。

看到一些代码并了解图像的来源以及在它们被读取后发生了什么可能会有所帮助。

英文:

It looks like you are trying to read binary data rather than textual data, so readLine() is not appropriate. As Henry points out in their comment, readLine() uses one of the read methods, so cannot possibly be faster that all of them. Try one of the read operations that takes a buffer (byte[]) as an argument.

If that does not solve it, consider whether it could be the speed of the server or if the speed is rate-limited not by the call to read() but by what happens once the data have been read. If you are reading only at the speed at which your code can process then the later stages of your processing pipeline may be the issue.

It might help to see some of the code and learn more about the source of the images and what happens to them after they are read.

答案4

得分: 0

从文件中读取内容时,绝对不要使用字符串连接操作符 + 来构建响应字符串。由于字符串是不可变的,每次使用 + 都会创建一个新的字符串对象,即使只是添加一个字符。

永远不要使用 int read() 方法或任何其他每次读取或写入单个字符的方法。每个字符都会增加一定的额外开销,即使使用了像 BufferedReader 这样包含缓冲区的流,这个开销也不会完全消失。

你应该做的是在读取时使用一个足够大的缓冲区(通常为1-2k),并使用 StringBuilder 类来构建字符串,就像这样:

mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ISO-8859-1"));

char[] buffer = new char[1500];
int read;
StringBuilder strBuilder = new StringBuilder();
while ((read = mBufferIn.read(buffer)) > -1) {
    strBuilder.append(buffer, 0, read);
}
mServerMessage = strBuilder.toString();
英文:

Never ever use string concatenation with + to construct the response when reading from a file. Since String is immutable, using + has to create a new string every time, just to add a single character.

Never use the int read() method or any other method that reads or writes single characters at a time. The added overhead per character does not go away completely, not even when you use a stream that incorporates a buffer like BufferedReader.

What you should do is use a buffer of a non-trivial size (1-2k is typically enough) when you read, and construct strings using the StringBuilder class. Like this:

mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ISO-8859-1"));

char[] buffer = new char[1500];
int read;
StringBuilder strBuilder = new StringBuilder();
while ((read = mBufferIn.read(buffer)) > -1) {
    strBuilder.append(buffer, 0, read);
}
mServerMessage = strBuilder.toString();

</details>



huangapple
  • 本文由 发表于 2020年7月22日 21:33:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/63035428.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定