目录
IO流分类
字节流:可以操作任何文件
字符流:只能操作文本文件(记事本可以直接打开的文件)
字节输出流
快速入门:使用字节流向文件写入数据
//使用字节输出流
@Test
public void test1() throws Exception {
FileOutputStream fileOutputStream = new FileOutputStream("a.txt");
fileOutputStream.write(97);
fileOutputStream.close();
}
idea中文件的相对路径
细节:
创建输出对象的时候:1.可以是字符串或者new File 2.文件不存在时会自动创建但是父文件必须存
在 3.文件已经存在则必须被清空
写数据:参数虽然是整数,但是写入文件的是ascii码所对应的字符
关闭流:每次都要关闭流,要不然会一直占用该文件
使用byte[]数组写入
/**
* fileOutputStream.write(byte[], off, len)
* off:开始索引 len:长度
* 当只有byte[]的时候则会将数组全部写入文件
*/
byte[] bytes = new byte[]{97, 98, 99, 77, 16};
fileOutputStream.write(bytes, 1, 2);
fileOutputStream.close();
续写和换行
换行:在windows中换行符为"\n",将其转为byte[]后写入文件中
续写:在写入文件的时候加上参数True
FileOutputStream fileOutputStream = new FileOutputStream("a.txt", true);
String s = "kangelaoyezuisuai";
byte[] bytes = s.getBytes();
fileOutputStream.write(bytes, 1, 2);
//使用换行符换行
String ss = "\n";
byte[] bytes1 = ss.getBytes();
fileOutputStream.write(bytes1);
//再次写入
fileOutputStream.write(bytes, 1, 2);
fileOutputStream.close();
字节输入流
read会向文件中一个一个的读取,当读取不到的时候返回-1
FileInputStream fis = new FileInputStream("a.txt");
int read = fis.read();
System.out.println((char)read);
read = fis.read();
System.out.println((char)read);
read = fis.read();
System.out.println((char)read);
read = fis.read();
System.out.println((char)read);
read = fis.read();
System.out.println((char)read);
read = fis.read();
System.out.println((char)read);
//如果读取结束会返回-1
int i = fis.read();
System.out.println(i);
fis.close();
细节:1.如果文件不存在则会直接报错 2.程序读取的是数据在ascii上对应的数字
字节流的循环读取
FileInputStream fis = new FileInputStream("a.txt");
//字节输入流的循环读取
int b;
while((b = fis.read()) != -1){
System.out.print((char)b);
}
fis.close();
文件拷贝
//文件拷贝
FileInputStream fileInputStream = new FileInputStream("a.txt");
FileOutputStream fileOutputStream = new FileOutputStream("b.txt");
int b;
while((b = fileInputStream.read()) != -1){
fileOutputStream.write(b);
}
fileOutputStream.close();
fileInputStream.close();
使用fileInputStream对象的read方法读取byte[]数组,返回值len表示读取到了几个
我们在a.txt文件中写入abcdefa使用byte[]进行读取
//使用bytes[]数组对文件进行读取,返回值为读取的个数
FileInputStream fileInputStream = new FileInputStream("a.txt");
byte[] b = new byte[2];//表示一次读取两个字节
int read = fileInputStream.read(b);
System.out.println(new String(b) + " " + read);
read = fileInputStream.read(b);
System.out.println(new String(b) + " " + read);
read = fileInputStream.read(b);
System.out.println(new String(b) + " " + read);
read = fileInputStream.read(b);
System.out.println(new String(b) + " " + read);
fileInputStream.close();
结果如下:
最后结果返回af和值1,这是因为在byte[]数组中每次都是覆盖操作,最后只读到了1个字节,并被返回,在byte[]中也只会覆盖一个后面还是原来旧数据
我们使用byte[]对文件拷贝进行改进
//使用byte[]数组的文件拷贝
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\lenovo\\Desktop\\e824fa78757ba86b7d6049ed60d90c2.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("i.jpg");
int len;
byte[] b = new byte[1024 * 1024 * 5];
while((len = fileInputStream.read(b)) != -1){
fileOutputStream.write(b, 0, len);
}
//先打开的后关闭
fileOutputStream.close();
fileInputStream.close();
异常捕获
因为IO流的关闭在最后必须被操作,所以我们将他们移到finally中;通道也可能未被创建,所以我们在关闭之前需要判断是否为空
idea中一般的写法
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
fileInputStream = new FileInputStream("C:\\Users\\lenovo\\Desktop\\e824fa78757ba86b7d6049ed60d90c2.jpg");
fileOutputStream = new FileOutputStream("i.jpg");
int len;
byte[] b = new byte[1024 * 1024 * 5];
while ((len = fileInputStream.read(b)) != -1) {
fileOutputStream.write(b, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//先打开的后关闭
if (fileOutputStream != null) fileOutputStream.close();
if (fileInputStream != null) fileInputStream.close();
}
实现AutoCloseAble的对象可以将流放在try中可以实现自动关闭,写法如下
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\lenovo\\Desktop\\e824fa78757ba86b7d6049ed60d90c2.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("i.jpg");
try (fileOutputStream; fileInputStream) {
int len;
byte[] b = new byte[1024 * 1024 * 5];
while ((len = fileInputStream.read(b)) != -1) {
fileOutputStream.write(b, 0, len);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
字符集
GBK
汉字:在计算机中以两个字节存储,并以1开始
英文:在计算机中以一个字节存储,并以0开始
编码:将数字转为计算机内存储的 解码:将计算机存储的转为对应的数字
unicode
英文:一个字节 0XXXXXXX
中文:三个字节 1110XXXX 10XXXXXX 10XXXXXX
编码和解码
//编码
String s = "ai爱你哟";
byte[] bytes = s.getBytes();//在idea中默认使用UTF-8进行编码
byte[] bytes1 = s.getBytes("GBK");//在ecplise中默认使用GBK进行编码
System.out.println("UFT-8: " + Arrays.toString(bytes));
System.out.println("GBK: " + Arrays.toString(bytes1));
/*UFT-8: [97, 105, -25, -120, -79, -28, -67, -96, -27, -109, -97]
GBK: [97, 105, -80, -82, -60, -29, -45, -76]*/
//解码
String s1 = new String(bytes);//idea中默认也是UTF-8解码
String s2 = new String(bytes, "GBK");
System.out.println("UTF-8: " + s1);
System.out.println("UTF-8: " + s2);//编码方式和解码方式不同的时候出现乱码
/*UTF-8: ai爱你哟
UTF-8: ai鐖变綘鍝�*/
字符流
FileRead读取数据
/**
* 细节:
* read()1.在读取的时候仍是按字节读取,英文一次读一个字节,中文一次读三个字节
* 2.在读取解码后,自动会转为十进制,我们使用char进行强转
* 3.使用char[]进行读取数据,相当于直接用read()读取和强转
*/
FileReader fileReader = new FileReader("a.txt");
int read;
while((read = fileReader.read()) != -1){
System.out.print((char)read);
}
fileReader.close();
/*里面的各个人说话真好听
都是人才*/
使用char[]数组进行读取
FileReader fileReader = new FileReader("a.txt");
int read;
//使用char[]读取
fileReader = new FileReader("a.txt");
char[] c = new char[2];
while((read = fileReader.read(c)) != -1){
System.out.println(new String(c));
}
fileReader.close();
/**
* 里面
* 的各
* 个人
* 说话
* 真好
* 听
*
* 都是
* 人才
*/
使用字符流输出文件
/**
* 使用字符流写出数据
* void write(int c)
* void write(char[] c, int offend, int length)
* void write(String str, int offend, int length)
*/
FileWriter fileOutputStream = new FileWriter("b.txt",true);//续写
fileOutputStream.write(25105);//根据字符集的编码方式进行编码,把编码之后的数据写到文件中去
// fileOutputStream.close();
/**
* 输出结果:我
*/
fileOutputStream.write(new char[]{'a','b','c','d','我'});
// fileOutputStream.close();
/**
* 输出结果:abcd我
*/
fileOutputStream.write("你好呀");
/**
* 输出结果:你好呀
*/
fileOutputStream.close();
字符输出流缓冲区写出数据的条件:
1.缓冲区已满 2.使用flush() 3.使用close()
综合练习
使用字节流拷贝文件夹
//拷贝文件夹
File src = new File("D:\\src\\test1");
File desc = new File("D:\\src\\test2");
copydir(src, desc);
}
void copydir(File src, File desc) throws IOException {
desc.mkdir();
File[] files = src.listFiles();
for (File file : files) {
if(file.isFile()){
FileInputStream fileInputStream = new FileInputStream(file);
FileOutputStream fileOutputStream = new FileOutputStream(new File(desc, file.getName()));
byte[] b = new byte[1024];
int len = 1;
while((len = fileInputStream.read(b)) != -1){
fileOutputStream.write(b);
}
fileOutputStream.close();
fileInputStream.close();
}else{
copydir(file, new File(desc, file.getName()));
}
}
}
使用异或为文件加密
//文件的加密和解密,文件通过两次异或就可以恢复到原来
FileInputStream fileInputStream = new FileInputStream("2.jpg");
FileOutputStream fileOutputStream = new FileOutputStream("3.jpg");
int b;
while((b = fileInputStream.read()) != -1){
fileOutputStream.write(b ^ 100);
}
fileOutputStream.close();
fileInputStream.close();
缓冲流
缓冲流是对基本数据流的包装,初始化时其第一个参数是基本流、第二个参数是初始化缓冲区大小,使用缓冲流可以直接关闭基本数据流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.txt"));
int b;
while((b = bis.read()) != -1){
bos.write(b);
}
bis.close();
bos.close();
可以字节流一样,我们可以使用byte[]一次多个数据
字符缓冲流特有的两个方法:1.readline()一次读取一行,并自动删除换行符 2.newLine()自动换行
BufferedReader bufferedReader = new BufferedReader(new FileReader("a.txt"));
String s;
while((s = bufferedReader.readLine()) != null){
//readline会直接读取为String
System.out.println(s);
}
bufferedReader.close();
/**
* 输出结果:
* d-c-b-a
* adfsdf
* adfdf
*/
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("a.txt"));
String s1 = "nihaoa";
bufferedWriter.write(s1);
bufferedWriter.newLine();
String s2 = "safkjkfa";
bufferedWriter.write(s2);
bufferedWriter.newLine();//使用newLine换行
bufferedWriter.close();
综合练习
恢复文本文件中的顺序
BufferedReader fis = new BufferedReader(new FileReader("a.txt"));
String s;
Map<Integer, String> map = new TreeMap<>(new Comparator<Integer>() {//使用TreeMap对元素进行排序
@Override
public int compare(Integer o1, Integer o2) {
return Integer.valueOf(o1) - Integer.valueOf(o2);
}
});
while((s = fis.readLine()) != null){
map.put(Integer.parseInt(s.substring(0,1)), s);
}
fis.close();
//重新写入文件
BufferedWriter bos = new BufferedWriter(new FileWriter("a.txt"));
map.entrySet().stream().forEach(p -> {
try {
bos.write(p.getValue());
bos.newLine();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
bos.close();
/*输出1.a
2.e
3.b
4.d*/
IO流原则:什么时候用什么时候打开,我们同时输出输入同一个文件会得到空
转换流
我们利用JDK11中的新特征实现转换流,FileRead/FileWriter
//1.较老的写法
InputStreamReader fis = new InputStreamReader(new FileInputStream("c.txt"), "GBK");//按GBK文件形式读取
int c;
while ((c = fis.read()) != -1) {
System.out.print((char) c);
}
fis.close();
//2.jdk11以后推荐使用这种方式:使用FileReader直接读取
FileReader fileReader = new FileReader("c.txt", Charset.forName("GBK"));
while ((c = fileReader.read()) != -1) {
System.out.print((char) c);
}
fileReader.close();
/**
* 输出结果:你好啊你好啊
*/
//使用转换流实现向文件输出数据
FileWriter fileWriter = new FileWriter("c.txt", Charset.forName("GBK"));
fileWriter.write("你好啊!!!!");
fileWriter.close();
使用转换流实现读取GBK文件后将文件以UTF-8的形式输出
//使用转换流实现读取GBK文件后将文件以UTF-8的形式输出
FileReader fileReader1 = new FileReader("c.txt", Charset.forName("GBK"));//以GBK形式读入
char[] c = new char[2];
int read = fileReader1.read(c);
fileReader1.close();
FileWriter fileWriter = new FileWriter("c.txt");
fileWriter.write(new String(c));//以默认utf-8形式写出
fileWriter.close();
练习:使用字节流读取文件中的数据,每次读取一行,不能出现乱码
//练习:使用字节流读取文件中的数据,每次读取一行,不能出现乱码
FileInputStream fileInputStream = new FileInputStream("c.txt");//读取为字节流
InputStreamReader gBk = new InputStreamReader(fileInputStream, "UTF-8");//为了不出现乱码,我们需要使用装换流转为字符流
BufferedReader bufferedReader = new BufferedReader(gBk);//将字符流转为缓冲流实现整行读取
String s = bufferedReader.readLine();
System.out.println(s);
bufferedReader.close();
序列化流
将对象写入文件中:对象需要实现Serializable标记接口
ObjectOutPutStream(OutPutStream out)将基本数据流包装为序列化流
writeObject(Object object)将对象写入文件
public class test1 {
@Test
public void test1() throws Exception {
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("c.txt"));
Person chen = new Person("chen", 22);
stream.writeObject(chen);
stream.close();
}
}
class Person implements Serializable {
String name;
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
}
反序列化流和上面相似
//生成对象流
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("c.txt"));
//从对象流中获取对象
Object o = objectInputStream.readObject();
System.out.println(o);
objectInputStream.close();
我们在序列化对象的时候除了Serializable标记接口,还要添加版本号,这样我们需要修改类后就不会报错了
private static final long serialVersionUID = 8683451231122892189L;
不需要的属性我们使用transient进行标注
transient String sex;//取消序列化
完整代码如下
public class test1 {
//读取序列化流
@Test
public void test1() throws Exception {
//生成对象流
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("c.txt"));
//从对象流中获取对象
Object o = objectInputStream.readObject();
System.out.println(o);
objectInputStream.close();
/**
* 输出结果:
* Person{name='小明', age=12, sex='null'}
*/
}
//写入序列化流
@Test
public void test2() throws IOException {
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("c.txt"));
Person p = new Person("小明", 12, "男");
stream.writeObject(p);
stream.close();
}
}
class Person implements Serializable {
private static final long serialVersionUID = 3933706312317968362L;//全局唯一版本号
String name;
int age;
transient String sex;//取消序列化
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
综合练习:将多个对象写入文件,但是我们在读的时候怎么解决都多少个的问题呢,这个时候我们就可以使用ArrayList实现了
public class test1 {
//读取序列化流
@Test
public void test1() throws Exception {
//生成对象流
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("c.txt"));
//从对象流中获取对象
Object o = objectInputStream.readObject();
ArrayList<Person> list = (ArrayList<Person>)o;//将对象强转
for(Person p: list){
System.out.println(p);
}
objectInputStream.close();
/**
* 输出结果:
* Person{name='小明', age=12, sex='null'}
*/
}
//写入序列化流
@Test
public void test2() throws IOException {
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("c.txt"));
Person p1 = new Person("小明", 12, "男");
Person p2 = new Person("小红", 12, "男");
Person p3 = new Person("小绿", 12, "男");
List<Person> list = new ArrayList<>();
list.add(p1);
list.add(p2);
list.add(p3);
stream.writeObject(list);//我们直接将ArrayList写入文件中
stream.close();
}
}
class Person implements Serializable {
private static final long serialVersionUID = 3933706312317968362L;//全局唯一版本号
String name;
int age;
transient String sex;//取消序列化
public Person(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
打印流
字节打印流PrintStream/字符打印流PrintWriter 打印流的特点:
使用字节输出流printStream
PrintStream printStream = new PrintStream(new FileOutputStream("a.txt"), true, Charset.forName("UTF-8"));
printStream.println(97);//直接写+刷新+自动换行
printStream.print(true);//直接写
printStream.println();
printStream.printf("%s 爱 %s", "小明", "小红");
printStream.close();
/**
* 输出结果:
* 97
* true
* 小明 爱 小红
*/
字符打印流
字符打印流方法和字节打印流方法基本相同
//使用字符打印流要打开自动刷新
PrintWriter printWriter = new PrintWriter(new FileWriter("a.txt"), true);
printWriter.println("随便一句话");
printWriter.print("害");
printWriter.printf("%s 爱上 %s","a珍","a强");
printWriter.close();
解压缩流/压缩流
压缩包里每一个文件都是ZipEntry,解压缩的本质都是将压缩包内的文件解压缩到本地文件夹
使用解压缩流实现文件夹的解压缩
public void copy(File src, File desc) throws IOException {
ZipInputStream zip = new ZipInputStream(new FileInputStream(src));
ZipEntry zipEntry;
while((zipEntry = zip.getNextEntry()) != null){//为空则说明已经遍历完了
//如果是文件夹的话
if(zipEntry.isDirectory()){
File file = new File(desc, zipEntry.toString());
file.mkdir();//直接创建文件夹
}else{
//如果不是文件夹直接拷贝文件
FileOutputStream fileOutputStream = new FileOutputStream(new File(desc, zipEntry.toString()));
int c;//一个一个字节的拷贝
while((c = zip.read()) != -1){
fileOutputStream.write(c);
}
fileOutputStream.close();
//表示压缩包内的一个文件处理完了
zip.closeEntry();
}
}
zip.close();
}
压缩文件夹
//压缩文件夹
@Test
public void test7() throws IOException {
//要拷贝的文件
File file = new File("D:\\src\\test4");
ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(new File(file.getParent(), file.getName() + ".zip")));
//注意这里要多加一个参数,否则后面文件不好命名
fileToZip(file, zipOutputStream, file.getName());
zipOutputStream.close();
}
void fileToZip(File src, ZipOutputStream desc, String name) throws IOException {
File[] files = src.listFiles();
for(File file: files){
if(file.isFile()){//如果是文件的话
ZipEntry zipEntry = new ZipEntry(new File(name, file.getName()).toString());
desc.putNextEntry(zipEntry);
int b = 0;
FileInputStream fileInputStream = new FileInputStream(file);
while((b = fileInputStream.read()) != -1){
desc.write(b);
}
desc.closeEntry();
fileInputStream.close();
}else {//如果是文件夹的话
fileToZip(file, desc, name + "//" + file.getName());
}
}
}
Commons-io
/*//拷贝文件
File src = new File("a.txt");
File desc = new File("copy.txt");
FileUtils.copyFile(src,desc);*/
//复制文件夹
/*File src = new File("D:\\src\\test4");
File desc = new File("D:\\src\\test6");
//copyDirectory拷贝的是文件夹内的内容,copy拷贝的是文件夹
FileUtils.copyToDirectory(src, desc);*/
//delete删除和clear清空文件夹
// FileUtils.cleanDirectory(new File("D:\\src\\test6\\test4"));
FileUtils.deleteDirectory(new File("D:\\src\\test6"));
hutool工具包
//hutool工具的使用
@Test
public void test9(){
//字符串拼接为file
// File file = FileUtil.file("D://", "a", "b", "c","a.txt");
// System.out.println(file);
//当父文件不存在的时候我们不可以创建,使用hutool就可以了
// File touch = FileUtil.touch(file);
// System.out.println(touch);
//将集合写入文件
ArrayList<String> arr = new ArrayList<>();
arr.add("aaa");
arr.add("aaa");
arr.add("aaa");
arr.add("aaa");
// FileUtil.writeLines(arr,"D:\\workspace\\workspce_idea\\workspace0119\\MyStructure\\YunAlgorithm\\a.txt","UTF-8");
//向文件后面追加集合
// File file = FileUtil.appendLines(arr, "D:\\workspace\\workspce_idea\\workspace0119\\MyStructure\\YunAlgorithm\\a.txt", "UTF-8");
//向文件中读取集合
ArrayList<String> strings = FileUtil.readLines(new File("D:\\workspace\\workspce_idea\\workspace0119\\MyStructure\\YunAlgorithm\\a.txt"), "UTF-8", new ArrayList<String>());
System.out.println(strings);
}