2011年3月4日星期五

Java判定文件编码或文本流编码并直接输出

主要原理利用JCharDet猜测文本流 
JCharDet 是mozilla自动字符集探测算法代码的java移植,其官方主页为:http://jchardet.sourceforge.net/

网上找到的教程都是只探测编码,而一般的需求都是找到编码后,利用编码信息,把文本流或者文本再读取出来,可是问题来了,在探测编码后,JCharDet会读取一部分文本流了,不能再回过头去读文本了。可能的办法是再重新读取一次文件,或者再原来的其他操作得到一个新的InputStream,不过这样做总觉得不舒服。
其实可以利用BufferedInputStream的mark()和reset()方法(BufferedInputStream支持此操作):
mark用于标记当前位置;在读取一定数量的数据(小于readlimit的数据)后使用reset可以回到mark标记的位置。

测试代码如下:
public class Test {
public static void main(String[] args) throws Exception {
File file = new File("F:\\test.lrc");
BufferedInputStream in = new BufferedInputStream(new FileInputStream(file));
CharsetDetector charDect = new CharsetDetector();
String encode = charDect.detectChineseCharset(in, 5120);
System.out.println("侦测到的编码: " + encode);
BufferedReader reader = new BufferedReader(new InputStreamReader(in,
encode));
while (true) {
String line = reader.readLine();
if (line == null)
break;
System.out.println(line);
}
reader.close();
in.close();
}
}

CharsetDetector类的代码如下(附带注释):
public class CharsetDetector {

private boolean found = false;
private String result;
private int lang;

/**
* @param imp
*            文本流需要封装成BufferedInputStream
* @param readlimit
*            探测的时候,最多用到多少,读到这个limit的时候就reset回去,供以后文本读取的方法读取,
*            一般测试下来5120应该就能检测出编码了,再多可能对性能有影响,
*            如果readlimit为负数,说明只要检测编码,不需要为BufferedInputStream mark,
*            不用文本读取,直接关闭流就可以了
* @param defaultCharset
*            默认的编码
*/
public String detectChineseCharset(BufferedInputStream imp, int readlimit,
String defaultCharset) throws IOException {
if (defaultCharset == null)
defaultCharset = "UTF-8";
if (readlimit > 0)
imp.mark(readlimit);
lang = nsPSMDetector.CHINESE;
// Initalize the nsDetector() ;
nsDetector det = new nsDetector(lang);
// Set an observer...
// The Notify() will be called when a matching charset is found.

det.Init(new nsICharsetDetectionObserver() {

public void Notify(String charset) {
found = true;
result = charset;
}
});
byte[] buf = new byte[1024];
int readRemain = readlimit;
boolean isAscii = true;
while (true) {
int bufLength = 1024;
if (readRemain > 0 && readRemain < bufLength)
bufLength = readRemain;
int len = imp.read(buf, 0, bufLength);
if (len == -1)
break;
readRemain -= len;
if (readRemain <= 0)
break;
// Check if the stream is only ascii.
if (isAscii)
isAscii = det.isAscii(buf, len);
// DoIt if non-ascii and not done yet.
if (!isAscii) {
if (det.DoIt(buf, len, false))
break;
}
}
if (readlimit > 0)
imp.reset();
else
imp.close();
det.DataEnd();
if (isAscii) {
found = true;
return defaultCharset;
} else if (found) {
return result;
} else {
String[] pc = det.getProbableCharsets();
if (pc == null || pc.length == 0)
return defaultCharset;
else
return pc[0];
}
}
}
发表评论