如题。具体情形是:我要读取一个大文本,于是每次读取一小部分(假设每次读取58kb),读取出来的形式是byte数组,然后再把byte数组转化为String。现在遇上的问题是一些特殊编码如unicode最开始读取到的58kb数据不是乱码,但是往后都是乱码了。经过查找资料得知,原因是unicode一个字符要占几个字节吧,由于我分块读取所以把分界点的字符拆开了,比如只读取了半个字符。应该这就是问题所在了吧。现在我考虑的办法是分析每块读取的byte数组最后长度为10的一部分如果没有半个字符就读完,如果有半个字符就归到下一块。只是我不知该如何区分,求助。或者你们觉得我的想法有错或者有更好的想法也可以提出来。编码类型现在考虑到的有UNICODE、UTF-16BE、UTF-16LE。值得注意的是现在我已经能判断这几种编码类型了,所以问题仅仅有以上所说的
如果可能,使用nio方便多了
你得这种方法可能只能自己从底层编代码实现。非得读取出来的形式是byte数组吗?
例如用BufferedReader.read(char[], int, int)方法读入到char数组不会出现“半个汉字”的问题。但是要自己控制读取的长度(比如58k)。如果不是一定要读取出到byte数组,用读入字符的方式,处理中西文混排的文本时不会有“半个汉字”的问题。
还有就是英文unicode编码的也会出现乱码
如何将字符归入下一块,这是数组操作,楼主应该问题不大。
如何判断字节数组是否残缺字符,楼主可以参照下面的代码: public static void main(String[] args){
byte[] source = new byte[]{(byte)0xE4, (byte)0xB8, (byte)0xAD}; //中(UTF-8)
byte[] source2 = new byte[]{(byte)0xE4, (byte)0xB8 };
byte[] source3 = new byte[]{(byte)0xE4 };
System.out.println( isValidChar(source, "UTF8")); //true
System.out.println( isValidChar(source2, "UTF8")); //false,残缺
System.out.println( isValidChar(source3, "UTF8")); //false,残缺
}
private static boolean isValidChar(byte[] bytes, String charset){
boolean res = true;
ByteBuffer bb = ByteBuffer.wrap(bytes);
CharsetDecoder utf8Decoder =
Charset.forName(charset).newDecoder().onMalformedInput(CodingErrorAction.REPORT);
try{
utf8Decoder.decode(bb);
}catch(Exception e){
res = false;
}
return res;
}
http://docs.oracle.com/javase/tutorial/i18n/text/stream.html
用字符流读取时打开文件流时指定字符集,由char[]转换为String时不指定编码,String的值在JVM内都是unicode编码。
/**
* @param name 文件名。
* @param charset 字符集。指定的字符集必须与name指定的文件的字符集一致,否则乱码。
*/
public void readDemo(String name, String charset) {
InputStreamReader instream = null; try {
FileInputStream fis = new FileInputStream(name);
instream = new InputStreamReader(fis, charset); int number;
char[] cbuf = new char[80];
// 示例,每次读入20个字符
while ((number = instream.read(cbuf, 0, 20)) != -1) {
String str = new String(cbuf, 0, number);
System.out.println(str);
}
} catch (FileNotFoundException e) {
// 处理异常...
} catch (UnsupportedEncodingException e) {
// 处理异常...
} catch (IOException e) {
// 处理异常...
} finally {
try {
if (instream != null)
instream.close();
} catch (IOException e) {
}
}
}
而是char[]
InputStream实现用BufferedInputStreamchar[] -> String和byte没区别
String的构造函数有
String(char[],offset,size)
这种情况下,
理论上你从文章中间找10个连续字节,判断出正确字符起点的可能性不大。
如
abcdefghij
可能ab cd ef gh ij是合法字符的同时bc de fg hi也是合法字符。这时,只有两个办法:
①从10个字节增加到1000个字节(举例),增加准确度,但依然不可能达到100%正确
②从前往后依次读取(也就是说,知道正确字符起点,仅需剔除结尾残字到下一块)但似乎不符合你的需求
还有考虑不同OS上的换行符不同的问题。
0x53, 0, 0x0A, (byte)0xA0
是两个合法Unicode字符“匀ઠ”
但是其中0, 0x0A子串就是换行符。
尽管这种字符组合基本不会出现,(Ops, 在我这个帖子中,它已经出现)
但是这个例子让我放弃了对楼主想法的赞同。
呵呵 你多虑了。首先换行符是唯一的,即使亿分之一的概率出现和换行符字节相同的字符那也是没关系的。因为换行符不是必须的。1.首先残缺字符肯定是在结尾;2.残缺字符的字节数组中不可能包含换行符。
所以我的方案是完美的。
按照约定先奉上我的代码
public int newLength(byte[] data,String charset){
String lastStr=" \n";
String nullStr=" ";
byte[] array=null;
byte[] nullByte=null;
try {
array=lastStr.getBytes(charset);
nullByte=nullStr.getBytes(charset);
} catch (UnsupportedEncodingException e) {
}
if(null!=array){
boolean flag=true;
int len1=data.length;
int len2=array.length;
byte[] temp=new byte[len2-nullByte.length];
System.arraycopy(array, nullByte.length, temp, 0, temp.length);
System.out.println("Array"+Arrays.toString(temp));
len2=temp.length;
int j=len2-1;
for(int i=len1-1;i>-1;i--){
flag=true;
if(j>-1&&data[i]==temp[j]){
j--;
}
else{
flag=false;
j=len2-1;
}
if(j==-1&&flag){
array=null;
nullByte=null;
return i+len2;
}
}
}
array=null;
nullByte=null;
return data.length;
}写得很罗嗦。我试了一下,结果第二页中英文不会出现乱码了(比之前好多了),但是中文出现乱码,翻回第一页正常。想不明白为什么,求各位大大赐教。难道说我的方案哪里还有漏洞么?还是代码写错了?
如果你是任意切块,随机进入任意一块的话,残缺字符可能也出现在块的头部。
OK,26楼所举例子确实是极端情况,不太会出现。怕的就是某日它真的出现了。就不和你讨论这个了,反正你也知道了。至于你的程序,
你的nullStr和lastStr为什么都要包含一个空格呢?
UNICODE、UTF-16BE、UTF-16LE 都是2byte一个字符
feff前缀是啥意思 还有就是我写错了 没想得到会遇上没匹配到换行符的情况 应该加个判断什么的
哎 当时我太着急了 因为我几乎每次都遇上别人问我干啥的 为什么要那样做,我就急了 。况且世界上那么多人怎么能都问呢 而且无奇不有是不是?很多情况是我们想不到的 因为不是当事人啊
首先 我可能必须要读取为byte了 因为我提供了一个功能 允许用户随时更改编码格式 我不是把string。getbyte的 而是使用当前缓存的byte数组来允许用户任意构造任意编码的text 这样能确保准确与快速 其次我用到了MappedByteBuffer来很方便很快速地任意读取某一块内容的 输入流要求有getChannel()这个方法才能获得MappedByteBuffer的实例。
String charsetName = "utf-8";
InputStreamReader reader = InputStreamReader(stream , String charsetName);
BufferedReader in = new BufferedReader(reader);
while((String line = in.readLine())!=null){
...
}
in.close();
stream.close();