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

  • 在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. 网络游戏开发-服务器(01)Asp.Net Core中的websocket,并封装一个简单的中间件

    先拉开MSDN的文档,大致读一遍 (https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets) WebSocket 是一 ...

  2. error:java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Long

    问题:mysql中id存的是int(10),java代码中的id为long,转不过去 解决:mysql中的int要是需要转到java中的long,需要选择unsigned这个选项,即if(unsign ...

  3. testng及JMeter使用之初体验

            这里只是简单的说下我再项目中使用testng的JMeter使用的情况, 主要的是使用JMeter的体验, 都是以截图和文字为说明的.   1, 性能测试之testng     直接可以 ...

  4. Apache中限制和允许特定IP访问

    Apache中限制和允许特定IP访问<Directory "/var/www">Options AllAllowOverride NoneOrder Deny,Allo ...

  5. Windows下python2.7安装64位mysqlclient

    在使用python调用mysql数据库的时候,需要引入对应的数据包 import MySQLdb 如果提示找不到对应的包,说明还需要安装对应的mysql数据库客户端操作的python包.在python ...

  6. Hibernate 一对一关联映射,mappedBy参数解析

    在最近java,SSH框架的学习中遇到了这样的一个问题,在Hibernate的开发中一对一关联映射的单向关联,主表会在次表新增一列次表的主键如下图,但是在双向关联中次表不会在表中创建主表的主键一列,这 ...

  7. Hashtable源码解析(JDK1.8)

    package java.util; import java.io.*; import java.util.concurrent.ThreadLocalRandom; import java.util ...

  8. 关于在linux下安装git,以及在idea上将项目部署到码云上

    GIT 基于对linux感兴趣,并且也考虑到以后从事开发后也会用到linux,着实在闲余之际学学linux.最近在用VM虚拟机环境下学做一个项目,在git上卡了一点时间,但同时也收获  了不少,下面写 ...

  9. HTML定位简介

    转载出处 定位一直是WEB标准应用中的难点,如果理不清楚定位那么可能应实现的效果实现不了,实现了的效果可能会走样.如果理清了定位的原理,那定位会让网页实现的更加完美. 定位的定义:在CSS中关于定位的 ...

  10. ASP.NET MVC编程——错误处理与日记

    ASP.NET MVC的错误处理应考虑到这几个方面:模型绑定期间发生的错误,未能路由到指定操作,针对控制器的错误处理.使用配置文件可以帮助我们处理异常,但是不够灵活和全面:使用HandleErrorA ...