目录
什么是IO流
IO流的体系
字节流
FileOutputStream
FileInputStream
字符集
字符流
FileReader
FileWriter
字节流和字符流的使用场景
什么是IO流
内存不能永久化存储,程序停止,数据丢失,所以要添加一个存档功能,存储到硬盘的文件,我们要知道文件在哪里和如何传输,文件的位置也就是File类:路径,而IO流就是解决如何传输的问题,IO流就是一个存储和读取数据的解决方案.因为IO流跟File是息息相关的,所以需要先回顾一下,可以查看这一篇文章Java File类
File 表示系统中的文件或者文件夹的路径
获取文件信息(大小,文件名,修改时间) (判断文件的类型) (创建文件/文件夹) (删除文件/文件夹) ...
但是要注意:File类只能对文件本身进行操作,不能读写文件里面存储的数据.如果我们要读写文件里面存储的数据就要学习本篇文章所学的IO流.
IO流 用于读写文件中的数据(可以读写文件,或网络中的数据)
问:IO流中,谁在读,谁在写?以谁为参照物看读写的方向呢?
答:以程序为参照物读写,回答内存也可以,因为程序就是运行在内存当中的
所以我们要进行分类,先让大家有一个整体的认知,再一种一种地去学习
IO流的体系
字节流
FileOutputStream
操作本地文件的字节输出流,可以把程序中的数据写到本地文件中
书写步骤:
- 创建字节输出流
- 写数据
- 释放资源
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
*演示:字节输出流FileOutputStream
* 实现需求:写出一段文字到本地文件中.(暂时不写中文)
*
* 实现步骤:
* 创建对象
* 写出数据
* 释放资源
*/
//1.创建对象
//写出 输出流 OutputStream
//本地文件 File
FileOutputStream fos = new FileOutputStream("Myio\\a.txt");
//2.写出数据
fos.write(97);
//3.释放资源
fos.close();
}
}
如果不释放资源:
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
void write(int b) 一次写一个字节数据
void write(byte[] b) 一次写一个字节数组数据
void write(byte[] b,int off,int len) 一次写一个字节数组的部分数据
数组 起始索引 个数
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("Myio\\a.txt");
//2.写出数据
// fos.write(97);//a
// fos.write(98);//b
byte[] bytes = {97,98,99,100,101};
// fos.write(bytes);
fos.write(bytes,1,2);//b c
//3.释放数据
fos.close();
}
}
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
换行写:
续写:
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("Myio\\a.txt",true);
//2.写出数据
String str = "asdfgh";
byte[] arr = str.getBytes();
// System.out.println(Arrays.toString(arr));
fos.write(arr);
//<h1>换行</h1>
//再次写出一个换行符
//windows: \r\n 回车换行
//Linux: \n
//Mac: \r
/*
细节:
在windows操作系统当中,java对回车换行进行了优化
虽然完整的是\r\n,但是我们写其中一个\r或者\n
java也可以实现换行,因为java在底层会补全
建议:
不要省略,还是写全了.
*/
/*
<h1>续写</h1>
如果想要续写,打开续写开关即可
开关位置:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件
*/
String wrap = "\r\n";
byte[] bytes = wrap.getBytes();
fos.write(bytes);
String str2 = "666";
byte[] arr2 = str2.getBytes();
fos.write(arr2);
//3.释放资源
fos.close();
}
}
FileInputStream
操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来.
书写步骤:
- 创建字节输入流对象
- 读数据
- 释放资源
import java.io.FileInputStream;
import java.io.IOException;
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
//1.创建对象FileInputStream
FileInputStream fis = new FileInputStream("Myio\\a.txt");
//2.读取数据
int b1 = fis.read();// 97
System.out.println(b1);
//3.释放资源
fis.close();
}
}
import java.io.FileInputStream;
import java.io.IOException;
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
字节输入流循环读取
*/
//1.创建对象
FileInputStream fis = new FileInputStream("Myio\\a.txt");
//2.循环读取
int b;
/**
* read :表示读取数据,而且是读取一个数据就移动一次指针
* 所以一定要定义一个第三方变量 b
*/
while((b=fis.read())!= -1){
System.out.print((char)b);
}
//3.释放资源
fis.close();
}
}
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
* 练习:
* 文件拷贝
* 把D:\yjy\movie.mp4拷贝到当前模块下
*
* 注意:
* 选择一个比较小的文件,不要太大,大文件拷贝后面会学
*
*
*/
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\yjy\\movie.mp4");
FileOutputStream fos = new FileOutputStream("Myio\\copy.mp4");
//2.拷贝
//核心思想:边读边写
int b;
while((b=fis.read())!=-1){
fos.write(b);
}
//3.释放资源
//规则:先开的流最后在关闭
fos.close();
fis.close();
//还可以统计一下拷贝时间,单位毫秒 复习System这个类里面的方法
}
}
因为FileInputStream一次读写一个字节,所以速度比较慢
修改成这样就可以避免啦!
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
*public int read(byte[] buffer) 一次读一个字节数组数据
*
*/
//1.创建对象
FileInputStream fis = new FileInputStream("Myio\\a.txt");
//2.读取数据
byte[] bytes = new byte[2];
//一次读取多个字节数据 具体越多 跟数组的长度有关
//返回值 本次读取到多少个字节数据
int len1 = fis.read(bytes);
System.out.println(len1);
String str1 = new String(bytes,0,len1);
System.out.println(str1);
int len2 = fis.read(bytes);
System.out.println(len2);
String str2 = new String(bytes,0,len2);
System.out.println(str2);
//3.释放资源
fis.close();
}
}
用这个方法改写一下拷贝文件的练习:
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\yjy\\movie.mp4");
FileOutputStream fos = new FileOutputStream("Myio\\copy.mp4");
//2.拷贝
int len;
byte[] bytes = new byte[1024*1024*5];
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
//3.释放资源
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
}
之前我们所有的异常都是直接抛出处理,但是也要学习一下try...catch异常处理
以后工作中都是抛出处理 会学springboot框架会把我们抛出的异常统一处理,这个代码我们只需要了解即可.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo1 {
public static void main(String[] args) {
//1.创建对象
FileInputStream fis=null;
FileOutputStream fos=null;
try {
long start = System.currentTimeMillis();
fis = new FileInputStream("D:\\yjy\\movie.mp4");
fos = new FileOutputStream("Myio\\copy.mp4");
//2.拷贝
int len;
byte[] bytes = new byte[1024*1024*5];
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
long end = System.currentTimeMillis();
System.out.println(end-start);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//3.释放资源
if(fos!=null){//防止如果文件不存在导致空指针异常
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(fis!=null){
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
还可以简化:
我们在前面说,字节流读取文件的时候,文件中不要有中文
想要知道乱码出现的原因,这就是要知道我们今天要说到的字符集
字符集
原因2:编码和解码的时候用的是不同的码表
如何不产生乱码?
- 不要用字节流读取文本文件
- 编码解码时使用同一个码表,同一个编码方式
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class ByteStreamDemo1 {
public static void main(String[] args) throws UnsupportedEncodingException {
/*
Java中编码的方法
public byte[] getBytes() 使用默认方法进行编码
public byte[] getBytes(String charsetName) 使用指定方法进行编码
Java中解码的方法
String(byte[] bytes) 使用默认方式进行解码
String(byte[] bytes,String,charseName) 使用指定方法进行解码
*/
//1.编码
String str = "ai你哟";
byte[] byte1 = str.getBytes(); // eclipse 是GBK Idea是UTF-8
System.out.println(Arrays.toString(byte1));
byte[] byte2 = str.getBytes("GBK");
System.out.println(Arrays.toString(byte2));
//解码
String str2 = new String(byte1);
System.out.println(str2);
String str3 = new String(byte1,"GBK");
System.out.println(str3);
}
}
第二个乱码原因直接都调成utf-8即可
那么我们来解决第一个问题,是否存在一种流,默认也是一次读取一个字节,当遇到中文时,一次读多个字节
字符流
FileReader
public class CharStream {
public static void main(String[] args) throws IOException {
//1.创建对象并关联本地文件
FileReader fr = new FileReader(new File("Myio\\a.txt"));
//2.读取数据 read()
//字符流的底层也是字节流,默认也是一个字节一个字节读取的
//如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读取三个字节
//read()细节:
//1.read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
//最终把这个十进制作为返回值
//这个十进制的数据也表示在字符集上的数字
//英文:文件里面二进制数据:0110 0001
// read()方法进行读取,解码并转成十进制97
//中文:文件里面的二进制数据 11100110 10110001 10001001
// read()方法进行读取,解码并转成十进制27721
//我如果想看到中文汉字,就是把这些十进制数据,再进行强转
int ch;
while((ch=fr.read())!=-1){
System.out.print((char)ch);
}
//3.释放资源
fr.close();
}
}
import java.io.FileReader;
import java.io.IOException;
public class CharStream {
public static void main(String[] args) throws IOException {
//1.创建对象
FileReader fr= new FileReader("Myio\\a.txt");
//2.读取数据
char[] chars = new char[2];
int len;
//read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
//空参的read+强转类型转换
while((len=fr.read(chars))!=-1){
//把数组中的数据变成字符串再进行打印
System.out.print(new String(chars,0,len));
}
//3.释放资源
fr.close();
}
}
FileWriter
public class CharStream {
public static void main(String[] args) throws IOException {
// FileOutputStream fos = new FileOutputStream("Myio\\a.txt");
//
// fos.write(97);//字节流 每次只能操作一个字符
//
// fos.close();
FileWriter fw = new FileWriter("Myio\\a.txt",true);
//fw.write(25105);//根据字符集的编码方式进行编码,把编码之后的数据写到文件中去
// fw.write("牛逼呀!");//符号也是分中文和英文的 中文3 英文1
char[] chars = {'a','b','c','d','我'};
fw.write(chars);
fw.close();
}
}
文本文件内容:ab我
问题一: 如果文件超出8192
问题2:如果在中间加了一个FileWriter
字节流和字符流的使用场景
import java.io.*;
public class CharStream {
public static void main(String[] args) throws IOException {
//拷贝一个文件夹,考虑子文件夹
//1.创建对象表示数据源
File src = new File("D:\\aaa\\src");
//2.创建对象表示目的地
File dest = new File("D:\\aaa\\dest");
//3.调用方法开始拷贝
copydir(src,dest);
}
/**
* 作用:拷贝文件夹
* 参数一:数据源
* 参数二:目的地
* @param src
* @param dest
*/
private static void copydir(File src, File dest) throws IOException {
dest.mkdirs();
//递归
//1.进入数据源
File[] files = src.listFiles();
//2.遍历数组
for (File file : files) {
if(file.isFile()){
//3.判断文件,拷贝 文件开始 文件结束
FileInputStream fis = new FileInputStream(file);//a.txt
FileOutputStream fos = new FileOutputStream(new File(dest,file.getName()));//也叫a.txt
byte[] bytes = new byte[1024];
int len;
while((len=fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}else{
//4.判断文件夹,递归
copydir(file,new File(dest,file.getName()));
}
}
}
}
加密代码
import java.io.*;
public class CharStream {
public static void main(String[] args) throws IOException {
/**
* ^ : 异或
* 两边相同:false
* 两边不同:true
*
*/
//100 : 1100100
//10: 1010
//______________
// 1101110
//十进制:110
// System.out.println(100^10);//110
// System.out.println(110^10);//100
//1.创建对象关联原始文件
FileInputStream fis = new FileInputStream("Myio\\冰冰.jpg");
//2.创建对象关联加密文件
FileOutputStream fos = new FileOutputStream("Myio\\ency.jpg");
//3.加密处理
int b;
while((b=fis.read())!=-1){
fos.write(b^2);
}
//释放资源
fos.close();
fis.close();
}
}
解密代码
import java.io.*;
public class CharStream {
public static void main(String[] args) throws IOException {
//1.创建对象关联原始文件
FileInputStream fis = new FileInputStream("Myio\\ency.jpg");
//2.创建对象关联加密文件
FileOutputStream fos = new FileOutputStream("Myio\\redu.jpg");
//3.加密处理
int b;
while((b=fis.read())!=-1){
fos.write(b^2);
}
//释放资源
fos.close();
fis.close();
}
}
import java.io.*;
import java.util.ArrayList;
import java.util.Collections;
public class CharStream {
public static void main(String[] args) throws IOException {
//1.读取数据
FileReader fr = new FileReader("Myio\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch=fr.read())!=-1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序
//2-1-9-4-7-8
String str = sb.toString();
String[] arrStr = str.split("-");
ArrayList<Integer> list = new ArrayList<>();
for (String s : arrStr) {
int i = Integer.parseInt(s);
list.add(i);
}
// System.out.println(list);
Collections.sort(list);
System.out.println(list);
//3.写出
FileWriter fw = new FileWriter("Myio\\a.txt");
for (int i = 0; i < list.size(); i++) {
if(i==list.size()){
fw.write(list.get(i) + "");
}else{
fw.write(list.get(i)+"-");
}
}
fw.close();
}
}
public class CharStream {
public static void main(String[] args) throws IOException {
//1.读取数据
FileReader fr = new FileReader("Myio\\a.txt");
StringBuilder sb = new StringBuilder();
int ch;
while((ch=fr.read())!=-1){
sb.append((char)ch);
}
fr.close();
System.out.println(sb);
//2.排序 Stream流
Integer[] arr = Arrays.stream(sb.toString().split("-"))
.map(Integer::parseInt).sorted().toArray(Integer[]::new);
// .map(new Function<String, Integer>() {
// @Override
// public Integer apply(String s) {
// return Integer.parseInt(s);
// }
// })
System.out.println(Arrays.toString(arr));
//3.写出
FileWriter fw = new FileWriter("Myio\\a.txt");
String s = Arrays.toString(arr).replace(", ","-");
String result = s.substring(1,s.length()-1);
System.out.println(s);
fw.write(result);
fw.close();
}
}
细节一:文件中的数据不要换行 因为会有\r\n
细节二:bom头 也就是在文件前有一个隐藏的字符标记 有隐藏的数据 里面记录了一些文件的信息,比如说字符编码
如果你选择了这个那很可能这个文件的大小会比你预算的要多因为包含bom头
idea中可以设置