1.前言

这一篇随笔是写 NIO 关于文件输入输出的总结

/*
总结:
1.io操作包括 socket io ,file io ;
2.在nio模型,file io使用fileChannel 管道 ,socket io 使用socketChannel管道,
3.在file io可以使用transferTo 或 transferFrom 实现管道向管道的的数据传输,但是别人说有可能传输数据不完成,不建议这样做;
4.GBK 为2 byte,如果是utf-8,最大长度是4字节 ,是可变的,需要循环判断获取字节长度后设置limit值才能获取完成的utf-8编码,否则会乱码,
5. buffer.compact();将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样
,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。也就是说把已经写入缓冲区但是还没有读的数据左移到开头,
然后将position设为该数据默认然后继续做写操作,不会覆盖这些数据
6. buffer.clear();调用的是clear()方法,position将被设回0,limit被设置成 capacity的值。换句话说,Buffer 被清空了。Buffer中的数据并未清除,
只是这些标记告诉我们可以从哪里开始往Buffer里写数据。如果Buffer中有一些未读的数据,调用clear()方法,数据将“被遗忘”,
意味着不再有任何标记会告诉你哪些数据被读过,哪些还没有。
7. buffer.flip();重置position,limit,capacity, 也就是limit= position,position=0,capacity=设好的最大值,
8. buffer.position();缓冲区的有定位位置 ,一般是position值小于limit值,否则无数据
9. buffer.limit(); 缓冲区的有限制位置 ,一般是position值小于limit值,否则无数据
10. 不论是读取还是写入,都是position 于limit之间的位置操作
11. buffer.capacity();缓冲区的最大容量 ,由ByteBuffer.allocate(【int】) 设置
12. buffer.hasRemaining();是否还有有效数据 ,即position值小于limit值
13.buffer缓冲区内部是字节流,使用buffer.array()从 缓冲区获取position 于limit之间的底层字节数组,然后使用 new String将字节数组转字符串
*/

2.操作

(1)把字符串写入文件[使用 buffer.put]

    /**
* 把字符串写入文件[使用 buffer.put]
*/
@Test
public void w1() {
try {
//=================================
// //new文件对象
// File file = new File("C:/Users/cen/Desktop/ww/", "write.txt");
// //字节输出流,意思是指JVM的内容输出到文件对象里面
// //如果文件存在 覆盖
// FileOutputStream fos = new FileOutputStream(file);
//
//上面几句等同于下面一句
FileOutputStream fos = new FileOutputStream("C:/Users/cen/Desktop/ww/write.txt");
//==============================================
//true表示追加,如果文件存在 向里面继续添加内容,不加默认是覆盖
// FileOutputStream fos=new FileOutputStream(file,true);
//获取通道,该通道允许写操作【根据字节流是输入还是输出决定是读通道还是写通道】
FileChannel fc = fos.getChannel();
//
// System.out.println("通道数量====" + fc.size());
//打印的结果是0
//
//allocate是使用JVM的的内存的,适合小文件;allocateDirect则是使用系统的内存的,适合大文件
//设置字节缓的大小
ByteBuffer buffer = ByteBuffer.allocate(1024);
// ByteBuffer.allocateDirect()
//控制台写入字符串
// Scanner sca = new Scanner(System.in);
// String str = sca.next();
// \n 转行符算是一个字符
String str = "sa34523ui2345672\n" +
"kkkkuuuushfjsdhjkfhs222333444555\n" +
"撒大噶和\n" +
"稍等哈肯定会介绍的几款圣诞卡十大科技";
//将字符串转成字节后,放入字节缓冲
buffer.put(str.getBytes(StandardCharsets.UTF_8));
// 此行语句一定要有,每次执行了这一句才可以做io操作
buffer.flip();
//通道对字节缓冲区里的内容写入文件对象里面
fc.write(buffer);
System.out.println("写入成功");
//清空缓冲块
buffer.clear();
//关闭管道
fc.close();
//关闭字节输出流
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}

(2)把字符串写入文件[使用 ByteBuffer.wrap ,不设置缓冲区大小]

    /**
* 把字符串写入文件[使用 ByteBuffer.wrap ,不设置缓冲区大小]
*
* @throws Exception
*/
@Test
public void w2() throws Exception {
// 构造一个传统的文件输出流
FileOutputStream out = new FileOutputStream("C:/Users/cen/Desktop/ww/write.txt");
// 通过文件输出流获取到对应的FileChannel,以NIO的方式来写文件
FileChannel channel = out.getChannel();
// 将数据写入到Buffer中
ByteBuffer buffer = ByteBuffer.wrap("hello world".getBytes());
// 通过FileChannel管道将Buffer中的数据写到输出流中去,持久化到磁盘中去
channel.write(buffer);
channel.close();
out.close();
}

(3)从文件中一次性读取字符串

 /**
* 从文件中一次性读取字符串
*/
public static void main(String[] args) throws Exception {
//==============================================
//new文件对象
File file = new File("C:/Users/cen/Desktop/ww/write2.txt");
//字节输入流
//意思是文件内容向JVM输入
FileInputStream fis = new FileInputStream(file);
//
//上面几句等同于下面一句
// FileInputStream fis = new FileInputStream("C:/Users/cen/Desktop/ww/write.txt");
//==============================================
//
//开启管道
FileChannel fc = fis.getChannel();
//设置字节缓冲区的大小,这里设为文件对象的字节长度
// ByteBuffer buffer = ByteBuffer.allocate((int) file.length());
ByteBuffer buffer = ByteBuffer.allocate(1024);
//管道将文件对象的内容读出并放入缓冲区里面,字符上限为上面缓冲区设置的最大值
//read()方法返回的是个抽象的整数,意为字符的长度/个数,当为空时返回-1
int nread = fc.read(buffer);
// System.out.println(buffer);
System.out.println("抽象整数==" + nread);
// buffer.array()是缓冲区获取底层byte[]数组,
// 然后使用 new String 将字节转成字符串
System.out.println(new String(buffer.array()));
//清除缓冲区
buffer.clear();
//关闭管道
fc.close();
//关闭字节输入流
fis.close(); }

(4)多次读取文件数据--解决utf-8中文乱码问题

 /**
* 多次读取文件数据--解决utf-8中文乱码问题
*/
@Test
public void readutf8Data() throws Exception {
//==============================================
//new文件对象
File file = new File("C:/Users/cen/Desktop/ww/write.txt");
//字节输入流
//意思是文件内容向JVM输入
FileInputStream fis = new FileInputStream(file);
//==============================================
//
//开启管道
FileChannel fc = fis.getChannel();
//如果设置太小的,会乱码【解决方式不靠谱】
//如果是utf-8,最大长度是4字节 ,GBK 为2 byte
//因此这里设置最小是4
ByteBuffer buffer = ByteBuffer.allocate(6);
//管道将文件对象的内容读出并放入缓冲区里面,字符上限为上面缓冲区设置的最大值
//read()方法返回的是个抽象的整数,意为字符的长度/个数,当为空时返回-1
int mread;
//此时管道读取数据放入缓冲,缓冲的定位position 不再是0 ,需要将limit设为position,而position需要恢复到0 ,
//形成 【 limit == 当前的position值【也等于mread】,position == 0 , cap 不变】
while ((mread = fc.read(buffer)) != -1) { byte b;
//计算出此时中文utf-8的编码长度后放入这里
int idx; //out可以bai改成任何不与保留关键字相同的字符,其中du标签out代表了这个for循环结构zhi体。理论上,标签可以标记任何结构体
//break out ;代表语句执行跳出整个for循环结构
out:
for (idx = buffer.position() - 1; idx >= 0; idx--) {
b = buffer.get(idx);
if ((b & 0xff) >> 7 == 0) { // 0xxxxxxx
break;
} if ((b & 0xff & 0xc0) == 0xc0) { // 11xxxxxx,110xxxxx、1110xxxx、11110xxx
idx -= 1;
break;
}
if ((b & 0xff & 0x80) == 0x80) {
for (int i = 1; i < 4; i++) {
b = buffer.get(idx - i);
if ((b & 0xff & 0xc0) == 0xc0) {
if ((b & 0xff) >> (5 + 1 - i) == 0xf >> (3 - i)) {
//break out ;代表语句执行跳出整个for循环结构
break out;
} else {
idx = idx - 1 - i;
//break out ;代表语句执行跳出整个for循环结构
break out;
}
}
}
}
} //缓冲区定位参数移位
buffer.flip();
//获取限制数,备份
int limit = buffer.limit();
//设限制数为上面计算出的utf-8字符的长度
buffer.limit(idx + 1); // 阻止读取跨界数据
//
System.out.println(Charset.forName("UTF-8").decode(buffer).toString());
// 恢复limit
buffer.limit(limit);
//compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。
// limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
//也就是说把已经写入缓冲区但是还没有读的数据左移到开头,然后将position设为该数据默认
//然后继续做写操作,不会覆盖这些数据
buffer.compact();
}
//清除缓冲区
buffer.clear();
//关闭管道
fc.close();
//关闭字节输入流
fis.close();
}

(5) 文件复制 【如果已经存在则覆盖,因此需要提前判断是否存在】

  /**
* 文件复制 【如果已经存在则覆盖,因此需要提前判断是否存在】
*/
@Test
public void copyFile() throws Exception {
//分别创建流
//字节输入流,用于从文件读取数据,即原地址文件【被拷贝的文件】
FileInputStream fis = new FileInputStream("C:/Users/cen/Desktop/ww/write.txt");
//字节输出流,用于从将数据输出到文件里,即目标地址文件【拷贝文件】
FileOutputStream fos = new FileOutputStream("C:/Users/cen/Desktop/ww/write_copy.txt");
//
//分别开启通道
FileChannel sourceFC = fis.getChannel();
FileChannel destFC = fos.getChannel();
//
//复制 【一个主动一个被动】
//从哪里转移到这里 【参数 0 ,其实是管道的数量,等同于 destFC.size()】
destFC.transferFrom(sourceFC, 0, sourceFC.size());
//从这里转移到哪里
// sourceFC.transferTo(destFC,0,sourceFC.size());
//
//关闭通道
sourceFC.close();
destFC.close();
//
// 关闭字节输入流
fis.close();
fos.close(); }

最新文章

  1. hadoop运行原理之Job运行(五) 任务调度
  2. iOS常见算法笔试问题
  3. [题解]UVa 10891 Game of Sum
  4. cocoapods 更新
  5. linux 运行可执行文件version `GLIBC_2.17&#39; not found
  6. ios每日一发--Leanclude数据云存储以及登录 注册账户
  7. 禁用ipv6
  8. 我与python3擦肩而过(二)—— csv文件头哪去啦?
  9. (三)java的数据类型
  10. [转载]破解TexturePacker加密资源
  11. 当setTimeout遇到闭包
  12. class.forname()方法的学习(转)
  13. WPF InkCanvas 画图 基础使用教程
  14. 201521123049 《JAVA程序设计》 第10周学习总结
  15. windows 本地构建hadoop-spark运行环境(hadoop-2.6, spark2.0)
  16. 关于CI框架访问数据库类提示Call to undefined function mysqli_init()
  17. 二丶Django~1
  18. BZOJ3864: Hero meet devil(dp套dp)
  19. SQL Server-索引故事的遥远由来,原来是这样的?(二十八)
  20. Android 基础 二 四大组件 Activity

热门文章

  1. ES6解构赋值的简单使用
  2. Sentry 开发者贡献指南 - 后端服务(Python/Go/Rust/NodeJS)
  3. 转置Table.Transpose(Power Query 之 M 语言)
  4. Table.SplitColumn拆分…Split…(Power Query 之 M 语言)
  5. JAVA获取文件byte数组并输出进行展示和文件下载
  6. MFC屏蔽按键ESC、ENTER、Alt+F4
  7. 【LeetCode】958. Check Completeness of a Binary Tree 解题报告(Python & C++)
  8. 【LeetCode】857. Minimum Cost to Hire K Workers 解题报告(Python)
  9. golang 数组的一些自问自答
  10. 教学日志:javaSE-java中的数据类型和运算符