我正在从HttpUrlConnection将以下文件https://www.reddit.com/r/tech/top.json?limit=100读入BufferedReader中。我已经读取了一些文件,但只读取了应有文件的1/10。如果我更改输入缓冲区的大小,它什么也不会改变-它只是在较小的块中打印相同的东西:
try{
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
StringBuilder sb = new StringBuilder();
int charsRead;
char[] inputBuffer = new char[500];
while(true) {
charsRead = reader.read(inputBuffer);
if(charsRead < 0) {
break;
}
if(charsRead > 0) {
sb.append(String.copyValueOf(inputBuffer, 0, charsRead));
Log.d(TAG, "Value read " + String.copyValueOf(inputBuffer, 0, charsRead));
}
}
reader.close();
return sb.toString();
} catch(Exception e){
e.printStackTrace();
}
我认为问题在于文本全部在一行上,因为它在json中的格式不正确,而BufferedReader只能花这么长时间。有什么办法解决吗?
答案 0 :(得分:0)
read()
应该继续读到charsRead > 0
。每次调用读取时,读取器都会标记上次读取的位置,下一次调用将从该位置开始,并一直持续到没有其他读取对象为止。它可以读取的大小没有限制。唯一的限制是数组的大小,但是文件的整体大小没有限制。
您可以尝试以下操作:
try(InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
int read = 0;
byte[] buffer = new byte[4096];
while((read = is.read(buffer)) > 0) {
baos.write(buffer, 0, read);
}
return new String(baos.toByteArray(), StandardCharsets.UTF_8);
} catch (Exception ex){}
上述方法仅使用流中的字节并将其读入输出流,然后从中创建字符串。
答案 1 :(得分:0)
我认为问题在于文本全部都在一行上,因为它在json中的格式不正确,而
BufferedReader
只能花这么长时间。
此说明不正确:
您一次不会读取一行,并且BufferedReader
不会将文本视为基于行。
即使您一次从BufferedReader
读取一行(即使用readLine()
),对行长的唯一限制也是Java {{ 1}}的长度(2 ^ 31-1个字符),以及堆的大小。
那到底是怎么回事?
不清楚,但是这里有一些可能性:
String
也的固有限制为2 ^ 31-1个字符。但是,(至少)在某些实现中,如果尝试将StringBuilder
增大到该限制之外,则会抛出StringBuilder
。 (这种行为似乎没有记录在案,从阅读Java 8的源代码中可以明显看出。)
也许您读取数据的速度太慢(例如,由于您的网络连接速度太慢),并且服务器正在超时连接。
也许服务器对它愿意在响应中发送的数据量有限制。
由于您没有提到任何例外情况,并且您似乎总是得到相同数量的数据,所以我怀疑第三个解释是正确的。
答案 2 :(得分:0)
我的猜测是您的默认平台字符集为UTF-8,并且出现了编码问题。对于远程内容,应指定编码,并且不应假定该编码等于计算机上的默认编码。
响应数据的字符集必须正确。为此,必须检查标题。默认值应为Latin-1,ISO-8859-1,但浏览器会解释为 作为Windows Latin-1,Cp-1252。
String charset = connection.getContentType().replace("^.*(charset=|$)", "");
if (charset.isEmpty()) {
charset = "Windows-1252"; // Windows Latin-1
}
然后,您可以更好地读取字节,因为与读取的字节数和读取的字符数没有确切的对应关系。如果缓冲区的末尾是代理对的第一个字符,即两个UTF-16字符,它们构成U + FFFF以上的Unicode字形,符号,代码点,我不知道它的效率基本的“修复”。
BufferedInputStream in = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[512];
while (true) {
int bytesRead = in.read(buffer);
if (bytesRead < 0) {
break;
}
if (bytesRead > 0) {
out.write(buffer, 0, bytesRead);
}
}
return out.toString(charset);
确实是安全的:
sb.append(inputBuffer, 0, charsRead);
(获取副本可能是修复尝试。)
通过char[500]
占用的内存几乎是byte[512]
的两倍。
我看到该网站在我的浏览器中使用gzip压缩。这对于诸如json之类的文本来说是有意义的。我通过设置请求标头 Accept-Encoding:gzip 来模仿它。
URL url = new URL("https://www.reddit.com/r/tech/top.json?limit=100");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Accept-Encoding", "gzip");
try (InputStream rawIn = connection.getInputStream()) {
String charset = connection.getContentType().replaceFirst("^.*?(charset=|$)", "");
if (charset.isEmpty()) {
charset = "Windows-1252"; // Windows Latin-1
}
boolean gzipped = "gzip".equals(connection.getContentEncoding());
System.out.println("gzip=" + gzipped);
try (InputStream in = gzipped ? new GZIPInputStream(rawIn)
: new BufferedInputStream(rawIn)) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] buffer = new byte[512];
while (true) {
int bytesRead = in.read(buffer);
if (bytesRead < 0) {
break;
}
if (bytesRead > 0) {
out.write(buffer, 0, bytesRead);
}
}
return out.toString(charset);
}
}
可能是因为gzip不符合“浏览器”的要求,因此错误地在响应中设置了压缩内容的内容长度。。这是一个错误。
答案 3 :(得分:0)
我建议使用3d派对Http客户端。它可以从字面上将您的代码减少到只有几行,而您不必担心所有这些小细节。最重要的是-有人已经写了您要编写的代码。而且它可以正常工作并且已经过测试。一些建议:
仅是演示使用MgntUtils库编写的代码,以简化操作。 (我测试了代码,它的工作原理很吸引人)
private static void testHttpClient() {
HttpClient client = new HttpClient();
client.setContentType("application/json; charset=utf-8");
client.setConnectionUrl("https://www.reddit.com/r/tech/top.json?limit=100");
String content = null;
try {
content = client.sendHttpRequest(HttpMethod.GET);
} catch (IOException e) {
content = client.getLastResponseMessage() + TextUtils.getStacktrace(e, false);
}
System.out.println(content);
}