需求
直接上代码
String url = "http://xxx.xxx/xx.txt"; //需要从网络读取的文件
Inputstream is = new URL(url).openStream;
//接下来,把Inputstream读成String
然而该stream的编码未知,且不同文件编码不同。默认使用utf-8或gb2312都会造成部分结果乱码。
solution
//BufferedInputStream支持mark,可以重复读取stream,避免网络请求2次
BufferedInputStream is = new BufferedInputStream(new URL(url).openStream());
is.mark(1000000);//10M
//第一次处理stream,得到encoding
String encoding = new TikaEncodingDetector().guessEncoding(is);
is.reset();
return IOUtils.toString(is, encoding);
解释
背景知识
心路历程
-
首先尝试stream或file有没有方法直接getEncoding,得到结论:
You cannot determine the encoding of a arbitrary byte stream. 如果理解了字符编码背景知识,这个应该很容易理解参考 -
那么如何得到一个流的编码,还是有人做出了工具:
<dependency>
<groupId>org.apache.any23</groupId>
<artifactId>apache-any23-encoding</artifactId>
<version>1.1</version>
</dependency>
public static Charset guessCharset(InputStream is) throws IOException {
return Charset.forName(new TikaEncodingDetector().guessEncoding(is));
}
guessEncoding函数名很灵性。原理是读一遍流,根据各种编码特点推断输入流的编码。既然是guess就不是100%准的,不过实用效果不错。感兴趣的同学可以深入一下。
- guessEncoding已经把stream读过一遍了,直接转string得到空字符串。又不想再发一次网络请求重新获取。然后,发现BufferedInputStream支持mark/reset, 重复读取。finished,完美。
参考:http://zhangbo-peipei-163-com.iteye.com/blog/2022460
副产品:IO流的关闭问题
FIleInputStream的finalize方法会调用close(),即gc时会帮你关闭io,but,并不建议这样做。理由:
- 资源没有被及时释放;
- finalize方法调用导致gc时间过长
it is not a good idea to rely on it because it runs unpredictably. 参考1
如何正确关闭流:https://javarevisited.blogspot.com/2014/10/right-way-to-close-inputstream-file-resource-in-java.html
网友评论