NIO:
- 通道 read write
- 缓冲区 put get
- 字符集
- 选择器
- Path接口
- Files工具类
- BasicFileAttributes文件属性类
/*
* NIO:不是替代IO,是对IO的一种补充,可以更精细的管控流和访问文件系统
*
* 通道 :读取/写入流的通道类 getChannel()方法 支持通道的类有:FileInputStream FileOutputStream Socket 常用方法有:读read() 写write()
* 缓冲区:依托通道--从流中读出的内容可放置缓冲区,缓冲区内的内容可写入流 常用缓冲区 ByteBuffer ...等对应7种基本类型的,以及 MappedByteBuffer用于将文件映射到缓冲区
缓冲区的三大要素:当前位置、界限、容量。常用方法有:容量capacity() 清空clear() 位置position() 重置rewind() 界限limit() 写put() 读get() 分配allocate() 切分slice()
* 字符集:依托字符集,将字节映射成字符 例如:UTF-8 GBK
* 选择器:适用于套接字的通道,使用选择器可以通过多个通道执行IO
*/
/**
* jdk7 增加了Path Files BasicFileAttributes
*
* Path接口:封装了路径,提供了操作路径的大量方法. Path的toFile() <--互转--> File的toPath()
获取Path对象的工厂方法:Paths.get(String pathname,String...parts); 或者 Paths.get(URI uri);
*
* Files工具类:更加细腻的管控文件的大量方法. jdk8中该类新增4个方法 list() walk() lines() find() 都返回Stream,可以用lambda表达式来处理业务
* 常用方法有:删除delete() 创建目录createDirectory() 创建文件createFile() 是否存在exists() 是否目录isDirectory() 是否文件isRegularFile()
文件大小size() 文件属性readAttributes() 是否可读isReadable() 是否可写isWritable() 是否隐藏的isHidden() 是否可执行的isExecutable()
文件拷贝copy() 文件移动move() 文件的流对象newInputStream() newOutputStream()
获取文件通道newByteChannel()
OpenOption文件打开时的一些选项:枚举值有:APPEND追加写入 CREATE_NEW文件不存在时就创建 CREATE老文件要有就干掉,创建新的
*
* 文件属性类:BasicFileAttributes 常用方法有:文件创建时间createTime() 文件最后修改时间lastModifiedTime() 文件最后访问时间lastAccessTime()
是否目录isDirectory() 是否文件isRegularFile() 文件大小size()
*/
/**
* BufferUnderflowException BufferOverflowException 错误原因:读取的长度超出了允许的长度
*
* 例如下面的代码:
* ByteBuffer buf = ByteBuffer.allocate(2); /这里只分配了2个字节
* buf.order(ByteOrder.LITTLE_ENDIAN);
* byte[] tmp = new byte[3];
* buf.get(tmp); //这里buf.get(tmp);却get了3个字节的数据。所以导致 java.nio.BufferUnderflowException 异常
*
* 如何解决这个问题呢?添加读取长度与 ByteBuffer 中可读取的长度的判断:例如:
* while (buf.remaining() > 0) { //如果每次读取1个字节,那就判断大于0就行;如果每次读取2个字节,那就判断大于1就行
* byte b = buf.get();
* }
*
* 总结:
* 当 ByteBuffer.remaining() 小于要读取或写入的长度时,再执行读取或写入操作都会产生异常;
* 读取则产生 java.nio.BufferUnderflowException 异常
* 写入则产生 java.nio.BufferOverflowException 异常
* 当 ByteBuffer.remaining()==0 时,不能再执行读取或写入操作
*/
/**
* \r 回车键 十进制表示为13 十六进制0x0D
* \n 换行键 十进制表示为10 十六进制0x0A
* windows系统 文件内容中的换行是\r\n
* unix系统 文件内容中的换行是/n
* mac系统 文件内容中的换行是/r
*/
/*
* NIO:不是替代IO,是对IO的一种补充,可以更精细的管控流和访问文件系统
*
* 通道 :读取/写入流的通道类 getChannel()方法 支持通道的类有:FileInputStream FileOutputStream Socket 常用方法有:读read() 写write()
* 缓冲区:依托通道--从流中读出的内容可放置缓冲区,缓冲区内的内容可写入流 常用缓冲区 ByteBuffer ...等对应7种基本类型的,以及 MappedByteBuffer用于将文件映射到缓冲区
缓冲区的三大要素:当前位置、界限、容量。常用方法有:容量capacity() 清空clear() 位置position() 重置rewind() 界限limit() 写put() 读get() 分配allocate() 切分slice()
* 字符集:依托字符集,将字节映射成字符 例如:UTF-8 GBK
* 选择器:适用于套接字的通道,使用选择器可以通过多个通道执行IO
*/
/**
* jdk7 增加了Path Files BasicFileAttributes
*
* Path接口:封装了路径,提供了操作路径的大量方法. Path的toFile() <--互转--> File的toPath()
获取Path对象的工厂方法:Paths.get(String pathname,String...parts); 或者 Paths.get(URI uri);
*
* Files工具类:更加细腻的管控文件的大量方法. jdk8中该类新增4个方法 list() walk() lines() find() 都返回Stream,可以用lambda表达式来处理业务
* 常用方法有:删除delete() 创建目录createDirectory() 创建文件createFile() 是否存在exists() 是否目录isDirectory() 是否文件isRegularFile()
文件大小size() 文件属性readAttributes() 是否可读isReadable() 是否可写isWritable() 是否隐藏的isHidden() 是否可执行的isExecutable()
文件拷贝copy() 文件移动move() 文件的流对象newInputStream() newOutputStream()
获取文件通道newByteChannel()
OpenOption文件打开时的一些选项:枚举值有:APPEND追加写入 CREATE_NEW文件不存在时就创建 CREATE老文件要有就干掉,创建新的
*
* 文件属性类:BasicFileAttributes 常用方法有:文件创建时间createTime() 文件最后修改时间lastModifiedTime() 文件最后访问时间lastAccessTime()
是否目录isDirectory() 是否文件isRegularFile() 文件大小size()
*/
/**
* BufferUnderflowException BufferOverflowException 错误原因:读取的长度超出了允许的长度
*
* 例如下面的代码:
* ByteBuffer buf = ByteBuffer.allocate(2); /这里只分配了2个字节
* buf.order(ByteOrder.LITTLE_ENDIAN);
* byte[] tmp = new byte[3];
* buf.get(tmp); //这里buf.get(tmp);却get了3个字节的数据。所以导致 java.nio.BufferUnderflowException 异常
*
* 如何解决这个问题呢?添加读取长度与 ByteBuffer 中可读取的长度的判断:例如:
* while (buf.remaining() > 0) { //如果每次读取1个字节,那就判断大于0就行;如果每次读取2个字节,那就判断大于1就行
* byte b = buf.get();
* }
*
* 总结:
* 当 ByteBuffer.remaining() 小于要读取或写入的长度时,再执行读取或写入操作都会产生异常;
* 读取则产生 java.nio.BufferUnderflowException 异常
* 写入则产生 java.nio.BufferOverflowException 异常
* 当 ByteBuffer.remaining()==0 时,不能再执行读取或写入操作
*/
/**
* \r 回车键 十进制表示为13 十六进制0x0D
* \n 换行键 十进制表示为10 十六进制0x0A
* windows系统 文件内容中的换行是\r\n
* unix系统 文件内容中的换行是\n
* mac系统 文件内容中的换行是\r
*/
/**
* 以下示例主要针对NIO的三方面的表现着手
*
* 1:基于通道使用NIO
* 2:基于流使用NIO
* 3:基于路径和文件来使用NIO
*/
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.util.*;
/**
* 基于通道读文件
* 一般步骤:
* a:封装Path
* b:获取通道getChannel(path)
* c:准备缓冲区Buffer
* d:通过通道读写文件fChannel.read(buf); fChannel.write(buf);
*/
public class NIOdemo
{
/**
* 通过缓冲区以及通道,实现读文件(读ISO8859-1格式的文件可以,读UTF/GBK格式保存的文件很麻烦)
*/
public static void readFileByByteBufferAndChannel(){
int count=0; //读写缓冲区时,缓冲区内部的游标不停的向前滚动,表示已经读到了多少个字节
int j=0; //第几次缓冲区已满
try(SeekableByteChannel fchan = Files.newByteChannel(Paths.get("test.txt")) ){ //获取通道
/*举例: 假定当前测试文件保存20个英文字母,系统默认ASNI编码格式,两行数据保存的. windows下换行时有(回车)(换行)两个字节,所以内容大小22
System.out.println("test.txt文件的内容:0123456789\\r\\naaaaaaaaaa (文件的大小:"+fchan.size()+") 其中: \\r回车一个字节 \\n换行一个字节");
System.out.println("文件内容windows下分成两行保存的,采用ASNI编码格式,如下:");
System.out.println("0123456789");
System.out.println("aaaaaaaaaa");
*/
long fSize = fchan.size();
String fileEncoding = judgeFileEncoding();
ByteBuffer buf = ByteBuffer.allocate(10); //准备缓冲区
byte[] tmpB = new byte[2]; //临时存放中文字符截断的前序字节(编码不同,有可能前序就1个字节,也有可能前序2个字节)
int k = 0; //计数器:临时保存上一字节的次数
/**
* buf.rewind()方法:为什么使用?因为想游标重置后,从缓冲区头开始干活(对buf的读/写都是时刻往后滚动游标的)
*/
do{
++j; //第几次从文件流读至缓冲区
buf.rewind(); //buf重置游标,归零处,为了从缓冲区头开始写
count = fchan.read(buf);
//System.out.println("\r\n当前第("+j+")次从文件流读至缓冲区,当前缓冲区位置是:"+buf.position());
if(count!=-1){
buf.rewind(); //buf重置游标,归零处,,为了从缓冲区头开始读
//消费缓冲区数据:
//这种形式处理ISO8859-1编码格式的文件是可以的,因为ISO8859-1是西欧标准,一共表示256个字符,都是单字节的字符(ISO8859-1是对ASCII(127个单字节的字符)的扩充)
//这种形式无法处理UTF格式的以及中文字符(原因是多字节字符,有可能每次缓冲区的最后1个字节或最后2个字节是断码的)
//byte[] bs = new byte[count];
//buf.get(bs);
//System.out.print(new String(bs));
/**
* 以下的这种形式:通过缓冲区的形式获取任意编码格式保存的文件内容。!!!太麻烦!!!
*/
if( fileEncoding.equals("Unicode") || fileEncoding.equals("UTF-16LE") || fileEncoding.equals("UTF-16BE") ){ //UTF-16或Unicode编码时,英文中文都占2个字节
//由于文件带有BOM信息保存成UTF-16LE编码格式的,所以文件头部的两个字节FFFE仅用来表示文件编码格式的,不是真正的文件内容要剔除掉
if( j==1 ){
System.out.print(fileEncoding+"文件内容头部的BOM信息:");
System.out.printf("%X",buf.get()); //丢弃FF
System.out.printf("%X",buf.get()); //丢弃FE
System.out.println();
}
do{
byte b1 = buf.get();
byte b2 = buf.get();
byte[] bs = {b1,b2};
System.out.print(new String(bs,fileEncoding));
}while(buf.position()!=count);
}
else if( fileEncoding.equals("gb2312") ){ //gb2312时,英文占1字节,中文占2个字节,文件头部没有编码格式
do{
if(k>0){
byte[] bs = {tmpB[0],buf.get()};
System.out.print(new String(bs,"gb2312"));
k=0;
}
byte b1 = buf.get();
if( b1>=0 ){
System.out.print((char)b1);
}
else{//中文处理
if( buf.position() == count ) {
tmpB[0] = b1;
++k;
}else{
byte b2 = buf.get();
byte[] bs = {b1,b2};
System.out.print(new String(bs,"gb2312"));
}
}
}while(buf.position()!=count);
}
/**
* UTF-8 有以下编码规则:
* 如果一个字节,最高位(第8位)为0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
* 如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
* 如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节
*/
else if( fileEncoding.equals("UTF-8") ){ //UTF-8编码时,英文占1字节,中文占3个字节
if( j==1 ){
System.out.print("UTF-8文件内容头部的BOM信息:");
System.out.printf("%X",buf.get()); //丢弃EF
System.out.printf("%X",buf.get()); //丢弃BB
System.out.printf("%X",buf.get()); //丢弃BF
System.out.println();
}
do{
if(k==1){
byte[] bs = {tmpB[0],buf.get()};
System.out.print(new String(bs,"UTF-8"));
k=0;
}
else if(k==2){
byte[] bs = {tmpB[0],tmpB[1],buf.get()};
System.out.print(new String(bs,"UTF-8"));
k=0;
}
byte b1 = buf.get();
byte b2 = 0;
if( ((b1>>>5&0x05) == 5 || (b1>>>5&0x05) == 4) && buf.remaining()==1 ){
b2 = buf.get();
}
if(b1>0) System.out.print((char)b1); //表示单字节的UTF-8格式时的一个字符
else{
if( buf.position() == count ) {
if( (b1>>>5&0x05) == 5 ){
tmpB[0] = b1;
++k;
tmpB[1] = b2;
++k;
}
else if( (b1>>>5&0x05) == 4 ){
tmpB[0] = b1;
++k;
}
}
else{
if( (b1>>>5&0x05) == 5 ){ //表示字节为111xxxxx 需要连续读取3个字节,代表一个字符
b2 = buf.get();
byte b3 = buf.get();
byte[] bs = {b1,b2,b3};
System.out.print(new String(bs,"UTF-8"));
}
else if( (b1>>>5&0x05) == 4 ){//表示字节为110xxxxx 需要连续读取2个字节,代表一个字符
b2 = buf.get();
byte[] bs = {b1,b2};
System.out.print(new String(bs,"UTF-8"));
}
}
}
}while(buf.position()!=count);
}
System.out.println("");
}
}while(count!=-1);
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 读取文件:NIO缓冲区和通道,文件内容一次性全部处理
*/
public static void readFileByByteBufferAndChannel2(){
int count=0; //读写缓冲区时,缓冲区内部的游标不停的向前滚动,表示已经读到了多少个字节
try(SeekableByteChannel fchan = Files.newByteChannel(Paths.get("test.txt")) ){ //获取通道
long fSize = fchan.size();
String fileEncoding = judgeFileEncoding();
ByteBuffer buf = ByteBuffer.allocate(10); //准备缓冲区
ArrayList<Byte> arr = new ArrayList();
do{
buf.rewind();
count = fchan.read(buf);
if(count!=-1){
buf.rewind(); //buf重置游标,归零处,,为了从缓冲区头开始读
byte[] bb = new byte[count];
buf.get(bb);
for (byte t:bb )
arr.add(t);
}
}while(count!=-1);
int startIndex = 0;
if( fileEncoding.equals("Unicode") || fileEncoding.equals("UTF-16LE") || fileEncoding.equals("UTF-16BE") )
startIndex = 2;
else if ( fileEncoding.equals("UTF-8") ){
startIndex = 3;
}
Object[] ob = arr.toArray() ;
byte[] by = new byte[ob.length-startIndex];
for(int u=startIndex;u<ob.length;u++)
by[u-startIndex] = ((Byte)ob[u]).byteValue();
System.out.println(new String(by,fileEncoding));
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 读取文件:NIO缓冲区和通道,文件内容逐行处理
*/
public static void readFileByByteBufferAndChannel3(){
int count=0; //读写缓冲区时,缓冲区内部的游标不停的向前滚动,表示已经读到了多少个字节
try(SeekableByteChannel fchan = Files.newByteChannel(Paths.get("test.txt")) ){ //获取通道
long fSize = fchan.size();
System.out.println("fSize is :" + fSize);
String fileEncoding = judgeFileEncoding();
ByteBuffer buf = ByteBuffer.allocate(10); //准备缓冲区
ArrayList<Byte> arr = new ArrayList();
int j = 0; //缓冲区是10大小时,循环多少次
int lineNum = 0; //文件内容的第几行
int sum = 0; //共读取的文件字节总数
boolean firstLineFlag = true; //第一行的标志
do{
++j;
//System.out.println("\r\ndo的第("+j+")次:\r\n");
int startIndex = 0;
if( firstLineFlag ){
if( fileEncoding.equals("Unicode") || fileEncoding.equals("UTF-16LE") || fileEncoding.equals("UTF-16BE") )
startIndex = 2;
else if ( fileEncoding.equals("UTF-8") ){
startIndex = 3;
}
firstLineFlag = false;
}
buf.rewind();
count = fchan.read(buf);
if(count!=-1){
buf.rewind(); //buf重置游标,归零处,,为了从缓冲区头开始读
//丢弃文件内容头部的BOM信息
if(startIndex==2){
//输出4位8进制的num
//printf("%04o/n",num);
//输出2位16进制的num
//printf("%02X",num);
System.out.print("文件内容头部BOM信息:");
System.out.printf( "%02X", buf.get() );
System.out.printf( "%02X\r\n", buf.get() );
sum = 2;
}
else if(startIndex==3){
buf.get();
buf.get();
buf.get();
sum = 3;
}
//处理不同系统文件内容中的换行符
if (System.getProperty("line.separator").equals("\r\n")) { //windows系统
while (buf.remaining()>0)
{
if( fileEncoding.equals("Unicode") || fileEncoding.equals("UTF-16LE") || fileEncoding.equals("UTF-16BE") ){
byte b1 = buf.get();
byte b2 = buf.get();
arr.add(b1);
arr.add(b2);
sum = sum+2;
byte judgeByte = b1;
if ( fileEncoding.equals("UTF-16BE") )
judgeByte = b2 ;
if(judgeByte==13){
if( buf.remaining()>0 ){
byte b3 = buf.get();
byte b4 = buf.get();
arr.add(b3);
arr.add(b4);
sum = sum+2;
byte[] by = listToArray(arr);
System.out.print("正统消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
arr.clear();
}
}
else if ( judgeByte==10 )
{
byte[] by = listToArray(arr);
System.out.print("跨缓冲区的行,消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
arr.clear();
}
else if (sum==fSize)
{
byte[] by = listToArray(arr);
System.out.print("结束消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
System.out.println();
return;
}
}
else{//非UTF-16编码
byte t1 = buf.get();
arr.add(t1);
sum++;
if( t1==13 ){
if( buf.remaining()>0 ){
byte t2 = buf.get();
arr.add(t2);
sum++;
byte[] by = listToArray(arr);
System.out.print("正统消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
arr.clear();
}
}
else if ( t1==10 )
{
byte[] by = listToArray(arr);
System.out.print("跨缓冲区的行,消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
arr.clear();
}
else if (sum==fSize)
{
byte[] by = listToArray(arr);
System.out.print("结束消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
System.out.println();
return;
}
}
}
}
else if (System.getProperty("line.separator").equals("/r")) {
while (buf.remaining()>0)
{
byte t1 = buf.get();
arr.add(t1);
sum++;
if( t1==13 ){ // /r换行符 mac系统下
if( buf.remaining()>0 ){
byte[] by = listToArray(arr);
System.out.print("正统消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
arr.clear();
}
}
else if (sum==fSize)
{
byte[] by = listToArray(arr);
System.out.print("/r结束消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
System.out.println();
return;
}
}
}
else if (System.getProperty("line.separator").equals("/n")) {
while (buf.remaining()>0)
{
byte t1 = buf.get();
arr.add(t1);
sum++;
if( t1==10 ){ // /n换行符 unix系统下
if( buf.remaining()>0 ){
byte[] by = listToArray(arr);
System.out.print("正统消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
arr.clear();
}
}
else if (sum==fSize)
{
byte[] by = listToArray(arr);
System.out.print("/n结束消费第("+(++lineNum)+")行:"+new String(by,fileEncoding)); //消费一行内容
System.out.println();
return;
}
}
}
}
}while(count!=-1);
}catch(Exception e){
e.printStackTrace();
}
}
private static byte[] listToArray(List<Byte> arr){
Object[] ob = arr.toArray() ;
byte[] by = new byte[ob.length];
for(int u=0;u<ob.length;u++)
by[u] = ((Byte)ob[u]).byteValue();
return by;
}
/**
* 文件映射到缓冲区:整个文件的内容(如果是UTF-8/UTF-16/Unicode编码格式保存的文件,内容头部有编码格式的字节码,除非是这三种形式无BOM保存时才没文件内容前缀)
* 文件内容头部字节码:3个字节EFBBBF表示UTF-8编码格式保存的文件
* 2个字节FFFE表示Unicode(windows系统默认Unicode为UTF-16LE格式)保存的文件,英文字母高8位字节补0
* 2个字节FEFF表示Unicode为UTF-16BE格式保存的文件,英文字母低8位字节补0
*/
public static void readFileByMappedByteBufferAndChannel(){
try(FileChannel fchan = (FileChannel)Files.newByteChannel(Paths.get("test.txt")) ){ //获取通道
String fileEncoding = judgeFileEncoding();
long fSize = fchan.size();
MappedByteBuffer buf = fchan.map( FileChannel.MapMode.READ_ONLY, 0, fSize ); //映射到缓冲区
ArrayList<Byte> arr = new ArrayList();
for(int i=0; i<fSize; i++){
if( fileEncoding.equals("Unicode") || fileEncoding.equals("UTF-16LE") || fileEncoding.equals("UTF-16BE") ){ //UTF-16或Unicode编码时,英文中文都占2个字节
if(i==0) {
System.out.print(fileEncoding+"文件内容头部的BOM信息:");
}
if(i<=1) {//由于使用了带有BOM内容消息头的保存的文件,所以文件头部的2个字节FFFE或FEFF仅用来表示文件编码格式的,不是真正的文件内容要剔除掉
System.out.printf("%X",buf.get());
continue;
}
if(i==2) System.out.println("\r\n\r\n文件内容如下:\r\n");
/*
* 每次循环读取两个字节来处理,逐个字符的消费模式
i=i+1;
byte b1 = buf.get();
byte b2 = buf.get();
byte[] bs = {b1,b2};
System.out.print(new String(bs,fileEncoding));
*/
/*
* 每次循环从缓冲区读出1个字节来消费,压入arrayList,一把消费整个文件内容字符的模式
*/
arr.add(buf.get());
if(buf.remaining()==0){
Object[] ob = arr.toArray() ;
byte[] by = new byte[ob.length];
for(int u=0;u<ob.length;u++)
by[u] = ((Byte)ob[u]).byteValue();
System.out.print(new String(by,fileEncoding));
}
}
else if( fileEncoding.equals("gb2312") ){ //gb2312时,英文占1字节,中文占2个字节,文件头部没有编码格式
byte b1 = buf.get();
if( b1>=0 ){
System.out.print((char)b1);
}
else{//中文处理
i=i+1;
byte b2 = buf.get();
byte[] bs = {b1,b2};
System.out.print(new String(bs));
}
}
/**
* UTF-8 有以下编码规则:
* 如果一个字节,最高位(第8位)为0,表示这是一个 ASCII 字符(00 - 7F)。可见,所有 ASCII 编码已经是 UTF-8 了。
* 如果一个字节,以 11 开头,连续的 1 的个数暗示这个字符的字节数,例如:110xxxxx 代表它是双字节 UTF-8 字符的首字节。
* 如果一个字节,以 10 开始,表示它不是首字节,需要向前查找才能得到当前字符的首字节
*/
else if( fileEncoding.equals("UTF-8") ){ //UTF-8编码时,英文占1字节,中文占3个字节
if(i==0) System.out.print("UTF-8文件内容头部的BOM信息:");
if(i<=2) {//由于使用了带有BOM内容消息头的保存的文件,所以文件头部的3个字节EFBBBF仅用来表示文件编码格式的,不是真正的文件内容要剔除掉
System.out.printf("%X",buf.get());
continue;
}
if(i==3) System.out.println("\r\n\r\n文件内容如下:\r\n");
/* UTF-8编码:逐个字符的消费模式
* 每次循环: ISO8859-1(包含ASCII)的字符为单字节,1字节的字符的消费模式
* 非中文且非ISO8859-1的字符为双字节,2字节的字符的消费模式
* 中文的字符为三字节,2字节的中文字符的消费模式
byte b1 = buf.get();
if(b1>0) System.out.print((char)b1); //表示ASCII码单字节的UTF-8格式时的一个字符
else{
if( (b1>>>5&0x01) == 1 ){ //表示字节为111xxxxx 需要连续读取3个字节,代表一个字符
i=i+2;
byte b2 = buf.get();
byte b3 = buf.get();
byte[] bs = {b1,b2,b3};
System.out.print(new String(bs,"UTF-8"));
}
else if( (b1>>>5&0x01) == 0 ){//表示字节为110xxxxx 需要连续读取2个字节,代表一个字符
i=i+1;
byte b2 = buf.get();
byte[] bs = {b1,b2};
System.out.print(new String(bs,"UTF-8"));
}
}
*/
/*
* 每次循环从缓冲区读出1个字节来消费,压入arrayList,一把消费整个文件内容字符的模式
*/
arr.add(buf.get());
if(buf.remaining()==0){
Object[] ob = arr.toArray() ;
byte[] by = new byte[ob.length];
for(int u=0;u<ob.length;u++)
by[u] = ((Byte)ob[u]).byteValue();
System.out.print(new String(by,fileEncoding));
}
}
}
System.out.println("");
}catch(Exception e){
e.printStackTrace();
}
}
/**
* 传统IO方式的读文件
*/
public static void readFileByBufferedReader(){
String fileEncoding = judgeFileEncoding();
boolean deleteBOM = false;
if( fileEncoding.equals("Unicode")
|| fileEncoding.equals("UTF-16LE")
|| fileEncoding.equals("UTF-16BE")
|| fileEncoding.equals("UTF-8")
){
deleteBOM = true;
}
try( BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("test.txt"),fileEncoding)) ){
StringBuffer sb = new StringBuffer();
String line ;
do{
line = br.readLine();
if(null!=line) {
if( deleteBOM ){ line = line.substring(1); } //清理文件内容头部的BOM信息
sb.append(line).append(System.getProperty("line.separator"));
}
}while(null!=line);
System.out.println("\r\n传统IO方式 test.txt content is: \r\n"+sb.toString());
}
catch(Exception e){
}
}
/**
* 判断文件的编码格式
* UTF-16BE (big endian), 俗称大头
* UTF-16LE(little endian), 俗称小头 这个是比较常用的(高8位表示ascii值,低8位为0) windows操作系统默认保存为Unicode时就是指的UTF-16LE
* 举例:
* ascii字母 'a' 16进制表示为0x61 当UTF-16BE表示时为0x0061
* 当UTF-16LE表示时为0x6100 据说是为了提高速度而迎合CPU的胃口, CPU就是这到倒着吃数据的
*/
public static String judgeFileEncoding(){
String fileEncoding = "gb2312"; //中国大陆的简体版中文windows操作系统,默认文件编码格式是ANSI,也就是简体中文的gb2312编码格式 英文占1个字节 简体中文占2个字节
try(InputStream inputStream = new FileInputStream("test.txt")){
byte[] head = new byte[3];
inputStream.read(head);
if( head[0] == -1 && head[1] == -2 ) //head[0]为0xFF head[1]为FE 刚开始的三个字节如果是FFFE打头的,说明文件内容是UTF-16保存的 类似Unicode编码 英文和中文都是占2个字节
fileEncoding = "UTF-16LE";
else if( head[0] == -2 && head[1] == -1 ) //head[0]为0xFE head[1]为FF 刚开始的三个字节如果是FEFF打头的,说明文件内容是Unicode保存的 英文和中文都是占2个字节
fileEncoding = "UTF-16BE";
else if( head[0]==-17 && head[1]==-69 && head[2] ==-65) //head[0]为0xEF head[1]为0xBB head[1]为0xBF 刚开始的三个字节如果是EFBBBF打头的,说明文件内容是UTF-8保存的 英文占1个字节 中文占3个字节
fileEncoding = "UTF-8";
}
catch(Exception e){}
return fileEncoding ;
}
public static void main(String[] args)
{
//读取文件:NIO缓冲区和通道,文件内容每个字符处理
//readFileByByteBufferAndChannel();
//读取文件:NIO缓冲区和通道,文件内容一次性全部处理(如果文件内容过大,建议使用逐行处理)
//readFileByByteBufferAndChannel2();
//读取文件:NIO缓冲区和通道,文件内容逐行处理
readFileByByteBufferAndChannel3();
//readFileByMappedByteBufferAndChannel();
//读取文件:传统IO模式,文件内容逐行处理
//readFileByBufferedReader();
//judgeFileEncoding();
/*
System.out.println("-17的十六进制输出"+Integer.toHexString(-17));
System.out.println("-69的十六进制输出"+Integer.toHexString(-69));
System.out.println("-65的十六进制输出"+Integer.toHexString(-65));
int a = -17;
System.out.println("-17的二进制输出"+Integer.toBinaryString(a));
System.out.println("-17的八进制输出"+Integer.toOctalString(a));
System.out.println("-17的十六进制输出"+Integer.toHexString(a));
*/
/*
UTF-8 (Unicode表示时如下)
- u4e00-u9fa5 (中文) 0x3400~0x4DB5
- x3130-x318F (韩文)
- xAC00-xD7A3 (韩文)
- u0800-u4e00 (日文)
*/
}
}