笔者最近在用多线程来计算中文文本的标点符号数目,遇到了以下问题:

  • 在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();
}
}
}

Java.nio-随机读写汉字的更多相关文章

  1. java中随机生成汉字

    main方法中使用: //随机生成100个汉字 String ss=""; for(int i=0;i<100;i++){ ss+=getChinese(i); } Syst ...

  2. java nio 缓冲区读写数据(图形详解)

    Position 您可以回想一下,缓冲区实际上就是美化了的数组.在从通道读取时,您将所读取的数据放到底层的数组中. position 变量跟踪已经写了多少数据.更准确地说,它指定了下一个字节将放到数组 ...

  3. Java基础之读文件——使用通道随机读写文件(RandomReadWrite)

    控制台程序,使用通道随机读写primes_backup.bin文件. import static java.nio.file.StandardOpenOption.*; import java.nio ...

  4. Java知多少(72)文件的随机读写

    Java.io 包提供了 RandomAccessFile 类用于随机文件的创建和访问.使用这个类,可以跳转到文件的任意位置读写数据.程序可以在随机文件中插入数据,而不会破坏该文件的其他数据.此外,程 ...

  5. Java编程的逻辑 (60) - 随机读写文件及其应用 - 实现一个简单的KV数据库

    本系列文章经补充和完善,已修订整理成书<Java编程的逻辑>,由机械工业出版社华章分社出版,于2018年1月上市热销,读者好评如潮!各大网店和书店有售,欢迎购买,京东自营链接:http:/ ...

  6. 【JavaNIO的深入研究4】内存映射文件I/O,大文件读写操作,Java nio之MappedByteBuffer,高效文件/内存映射

    内存映射文件能让你创建和修改那些因为太大而无法放入内存的文件.有了内存映射文件,你就可以认为文件已经全部读进了内存,然后把它当成一个非常大的数组来访问.这种解决办法能大大简化修改文件的代码.fileC ...

  7. java大文件读写操作,java nio 之MappedByteBuffer,高效文件/内存映射

    java处理大文件,一般用BufferedReader,BufferedInputStream这类带缓冲的Io类,不过如果文件超大的话,更快的方式是采用MappedByteBuffer. Mapped ...

  8. java 随机读写访问流及seek方法

    package stream; import java.io.File; import java.io.FileNotFoundException; import java.io.IOExceptio ...

  9. Java IO流之普通文件流和随机读写流区别

    普通文件流和随机读写流区别 普通文件流:http://blog.csdn.net/baidu_37107022/article/details/71056011 FileInputStream和Fil ...

随机推荐

  1. 解决:安装oracle客户端,提示“oracle11g所选的主目录位于基目录外”

    安装oracle客户端,第三步下一步的时候,提示"oracle11g所选的主目录位于基目录外": 默认  Oracle基目录:D:\app\xxx 原      软件位置:F:\p ...

  2. gulp详细入门

    gulp是基于Nodejs的自动任务运行器, 她能自动化地完成 javascript/coffee/sass/less/html/image/css 等文件的的测试.检查.合并.压缩.格式化.浏览器自 ...

  3. 微信小程序之生成图片分享

    通过社交软件分享的方式来进行营销小程序,是一个常用的运营途径.小程序本身支持直接将一个小程序的链接卡片分享至微信好友或微信群,然后别人就可以通过点击该卡片进入该小程序页面.但是小程序目前不支持直接分享 ...

  4. 【Unity与23种设计模式】访问者模式(Visitor)

    GoF中定义: "定义一个能够在一个对象结构中对于所有元素执行的操作.访问者让你可以定义一个新的操作,而不必更改到被操作元素的类接口." 暂时没有完全搞明白 直接上代码 //访问者 ...

  5. 流式处理新秀Flink原理与实践

    随着大数据技术在各行各业的广泛应用,要求能对海量数据进行实时处理的需求越来越多,同时数据处理的业务逻辑也越来越复杂,传统的批处理方式和早期的流式处理框架也越来越难以在延迟性.吞吐量.容错能力以及使用便 ...

  6. linux 目录详解

    /bin bin是binary的缩写.这个目录沿袭了UNIX系统的结构,存放着使用者最经常使用的命令.例如cp.ls.cat,等等. /boot 这里存放的是启动Linux时使用的一些核心文件. /d ...

  7. python web开发-flask读取txt文件内容

    某些情况下,需要读取flask网站要目录下的txt文件.但是直接在flask网站的目录下创建一个文件是无法访问的.从网站找了一些资料,最终发现通过写一个方法返回txt内容比较简单方便,不过此方法适用于 ...

  8. git 使用方式

    一.常用操作命令 1.初始化操作 git config --global user.name '<name>' # 设置提交者名称 git config --global user.ema ...

  9. Java线程中断机制-如何中断线程

    介绍: 对于线程一共分为五个状态:新建状态,就绪状态,阻塞状态,运行状态,死亡状态,有时候把阻塞状态又分为同步阻塞和等待阻塞. 有时想让主线程启动的一个子线程结束运行,我们就需要让这个子线程中断,不再 ...

  10. Dubbo学习1-Hello world

    前言 互联网技术到今天已经非常成熟和稳定了,其中为了解决高并发.大规模的服务请求,出现了微服务.RPC这样的分布式架构.今天就从头开始学习RPC框架dubbo. 为什么要学Dubbo 关于分布式的解决 ...