Java.nio-随机读写汉字
笔者最近在用多线程来计算中文文本的标点符号数目,遇到了以下问题:
- 在Windows下,文本中汉字通常采用Unicode编码,这就导致需要随机(RandomAccessFile)读取文本时,产生乱码现象。
- 多线程计算前(假设有2个线程),需要将文本内容尽量等分成2份,并输出到新的文件中,再进行计算。
总体思路:
- 规定一次读取的字节数,再在存储和输出时转化成GBK编码
- 由于RandomAccessFile可以随机定位读取起始点,当规定了一次读取的字节数,也就规定了读取结束点。
- 按行读取,每一行的字节有对应的数组保存,转化成GBK后,写入输出文本。
- 引入java.nio,在读取文件和转化编码时方便很多,笔者认为java.io也可以实现。
- 关于NIO的详细教程可以参考:NIO系列教程
- 本文引入java.nio.ByteBuffer,java.nio.channels.FileChannel,前者无需解释,后者为通道,相当于流。
具体代码实现如下:
package Yue.IO; import java.io.*; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; /** * 将文本内容尽量分成n份,使n个线程处理对应的文本 */ public class SplitFile { int fileNum; //分离的文件数 File fileIn = new File("E:\\白夜行.txt"); int bufSize; SplitFile(int threadsNum) { fileNum = threadsNum; bufSize = (int) (fileIn.length() / fileNum); //一次读取的字节数 } FileChannel fileChaIn, fileChaOut; ByteBuffer rBuffer, wBuffer; /*设置缓冲区,读文件时,最后一行往往不完整,需要将存在断点的那一行保存,与下一次读文本时的第一行合并*/ byte[] temp = new byte[0]; /** * 按行具体读出每一个线程所要处理的文本内容 * * @param NO Thread-NO */ public void readByLine(int NO) { String enter = "\n"; byte[] lineByte; //保存每一行读取内容 /*确认读取范围*/ try { RandomAccessFile raf = new RandomAccessFile(fileIn, "r"); raf.seek(NO * bufSize); //根据分离文本进程定位 fileChaIn = raf.getChannel(); rBuffer = ByteBuffer.allocate(bufSize); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } try { if (fileChaIn.read(rBuffer) != -1) { /*生成输出文件Part-No.txt*/ try { fileChaOut = new RandomAccessFile("E:\\Part-" + NO + ".txt", "rws").getChannel(); wBuffer = ByteBuffer.allocateDirect(bufSize); } catch (FileNotFoundException e) { e.printStackTrace(); } /*根据一次读取,确定本次输出的字节长度*/ int rSize = rBuffer.position(); byte[] bs = new byte[rSize]; rBuffer.rewind(); rBuffer.get(bs); rBuffer.clear(); int startNum = 0; int LF = 10; //换行符 int CR = 13; //回车符 boolean hasLF = false; //是否有换行符 for (int i = 0; i < rSize; i++) { if (bs[i] == LF) { hasLF = true; int tempNum = temp.length; int lineNum = i - startNum; lineByte = new byte[tempNum + lineNum]; //数组大小已经去掉换行符 /*把上一次读取保存在缓冲区的内容和本次读取的这一行的内容合并,保存到lineByte[]中*/ System.arraycopy(temp, 0, lineByte, 0, tempNum); temp = new byte[0]; System.arraycopy(bs, startNum, lineByte, tempNum, lineNum); /*把该行内容转换成String类型,写入输出文件中*/ String line = new String(lineByte, 0, lineByte.length, "GBK"); writeByLine(line + enter); /*过滤回车和换行*/ if (i == rSize - 1 && bs[i + 1] == CR) { startNum = i + 2; } else { startNum = i + 1; } } } /*对每次读取的最后一行做特殊处理,将未读完整的当前行不输出,保存在缓冲区中,与下一次读取时合并*/ if (hasLF) { temp = new byte[bs.length - startNum]; System.arraycopy(bs, startNum, temp, 0, temp.length); } else { /*兼容单次读取不足一行的情况*/ byte[] toTemp = new byte[bs.length + temp.length]; System.arraycopy(temp, 0, toTemp, 0, temp.length); System.arraycopy(bs, 0, toTemp, temp.length, bs.length); temp = toTemp; } } } catch (IOException e) { e.printStackTrace(); } finally { try { fileChaIn.close(); fileChaOut.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 写入输出文件 * * @param line 已转换成String类型的当前行文本内容 */ public void writeByLine(String line) { try { fileChaOut.write(wBuffer.wrap(line.getBytes("GBK")), fileChaOut.size()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
相关推荐
arctan0 2020-10-14
爱传文档 2020-05-08
xuMelon 2020-04-23
liuyong00 2020-01-16
唐宋源码清 2019-12-23
xuMelon 2019-12-21
洪宇 2019-12-13
fengshantao 2019-12-02
追寻水中桥 2019-12-01
晨风 2019-10-23
帅性而为号 2019-08-30
XHuiLin 2018-10-25
woyanyouxin 2016-10-05
88447114 2016-09-19
strburnchang 2016-01-05
chenhualeguan 2016-01-05
Mical0 2013-03-24
gary00 2013-03-09