IO流(十七)
IO流:存储和读取数据的解决方案。
IO流的作用:用于读写数据(本地文件,网络)
按流向分类:
- 输出流:写出,从程序到文件
- 输入流:读取,从文件到程序
按操作文件类型分类:
- 字节流:可操作所有类型的文件
- 字符流:只可操作纯文本文件(用Windows系统自带的记事本打开且能读懂的文件,如:txt,md,xml,lrc)
体系
IO流体系
- 字节流:可以拷贝任意类型的文件
- InputStream:字节输入流,抽象类
- 基本流:FileInputStream:操作本地文件的字节输入流
- 缓冲流:BufferedInputStream:字节缓冲输入流
- 反序列化流:ObjectInputStream
- 解压缩流:ZipInputStream
- OutputStream:字节输出流,抽象类
- 基本流:FileOutputStream:操作本地文件的字节输出流
- 缓冲流:BufferedOutputStream:字节缓冲输出流
- 序列化流:ObjectOutputStream
- 字节打印流:PrintStream
- 压缩流:ZipOutputStream
- InputStream:字节输入流,抽象类
- 字符流:读取纯文本文件中的数据,向纯文本文件写出数据
- Reader:字符输入流,抽象类
- 基本流:FileReader:操作本地文件的字符输入流
- 缓冲流:BufferedReader:字符缓冲输入流
- 转换流:InputStreamReader:转换输入流
- Writer:字符输出流,抽象类
- 基本流:FileWriter:操作本地文件的字符输出流
- 缓冲流:BufferedWriter:字符缓冲输出流
- 转换流:OutputStreamWriter:转换输出流
- 字符打印流:PrintWriter
- Reader:字符输入流,抽象类
字节流
字节输出流OutputStream
FileOutputStream
操作本地文件的字节输出流,可把程序中的数据写到本地文件。
步骤:
- 创建字节输出流对象,就像在两个地点之间修一条公路
- 参数可以是字符串表示的路径或File对象
- 若文件不存在,则会创建新文件,但要保证父级路径存在
- 若文件存在,则会清空文件
- 写数据,就像在公路上传输
- write方法的参数是整数,则会写入整数对应的ASCII字符
- 释放资源,就像把公路拆掉
- 每次用完流后,均要释放资源
范例
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("b.txt");
fos.write(97);
fos.close();
}
}
写数据的三种方式
方式一:
void write(int b)
说明:一次写一个字节数据
范例:
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("a.txt");
fos.write(97);
fos.close();
}
}
/*a.txt的内容
a
*/
方式二:
void write(byte[] b)
说明:一次写一个字节数组数据
范例:
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("a.txt");
byte[] arr={97,98,99,100};
fos.write(arr);
fos.close();
}
}
/*a.txt的内容
abcd
*/
方式三:
void write(byte[] b,int off,int len)
说明:一次写一个字节数组的部分数据,off表示起始索引,len表示长度
范例:
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileOutputStream fos=new FileOutputStream("a.txt");
byte[] arr={97,98,99,100};
fos.write(arr,1,2);
fos.close();
}
}
/*a.txt的内容
bc
*/
换行和续写
换行:再次写出换行符即可。
不同系统下的换行符不同:window是\r\n,linux是\n,Mac是\r。
在Windows操作系统中,java对回车换行进行优化,虽然完整的是\r\n,但写其中一个\n或\r,java在底层便会自动补全,也可实现换行,不过还是建议不要省略,直接写全。
续写:
若不想清空文件,想续写,打开续写开关即可,开关位置在创建对象的第二个参数,默认为false,手动传递true便会打开续写。
范例:
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
//换行
FileOutputStream fos1=new FileOutputStream("a.txt");
String string="ljsblog";
byte[] bytes=string.getBytes();
fos1.write(bytes);
String strLine="\r\n";
byte[] line=strLine.getBytes();
fos1.write(line);
fos1.write(bytes);
fos1.close();
//续写
FileOutputStream fos2=new FileOutputStream("a.txt",true);
String str="666";
byte[] bytes1=str.getBytes();
fos2.write(bytes1);
fos2.close();
}
}
/*a.txt内容
ljsblog
ljsblog666
*/
字节输入流InputStream
FileInputStream
操作本地文件的字节输入流,可把本地文件中的数据读取到程序中来。
步骤:
- 创建对象
- 若文件不存在,则直接报错
- 读取数据
- 一次读一个字节,读出来的是数据在ASCII上对应的数字
- 若读到问价末尾,read方法返回-1
- 释放资源
- 每次使用完流必须要释放资源
/*a.txt的内容
ab
*/
import java.io.FileInputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("a.txt");
int c1=fis.read();
int c2=fis.read();
int c3=fis.read();
System.out.println(c1+" "+c2+" "+c3);
System.out.println(""+(char)c1+(char)c2+(char)c3);
fis.close();
}
}
/*
97 98 -1
ab
*/
循环读取
/*a.txt内容
absafsaf
*/
import java.io.FileInputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("a.txt");
int c;
while((c=fis.read())!=-1){
System.out.print((char)c);
}
fis.close();
}
}
/*
absafsaf
*/
一次读多个字节
格式:
public int read()
说明:一次读一个字节数据。
格式:
public int read(byte[] buffer)
说明:一次读一个字节数组数据,每次读取的数量和字节数组长度有关,读取的内容会存入字节数组,返回值是本次读出数据的字节长度,字节数组长度尽量是1024的倍数。
范例:
/*a.txt内容
abcde
*/
import java.io.FileInputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("a.txt");
byte[] bytes=new byte[2];
int len1=fis.read(bytes);
System.out.println(len1);
System.out.println(new String(bytes,0,len1));
int len2=fis.read(bytes);
System.out.println(len2);
System.out.println(new String(bytes,0,len2));
int len3=fis.read(bytes);
System.out.println(len3);
System.out.println(new String(bytes,0,len3));
fis.close();
}
}
/*
2
ab
2
cd
1
e
*/
拷贝文件
//将source.mp4拷贝,拷贝文件名copy.mp4
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("source.mp4");
FileOutputStream fos=new FileOutputStream("copy.mp4");
byte[] bytes=new byte[1024*1024*10];//10M
int len=0;
while((len= fis.read(bytes))!=-1){
fos.write(bytes,0,len);
}
fos.close();
fis.close();
}
}
异常处理
AutoCloseable
AutoCloseable是接口。
特点:特定情况下,可自动释放资源
基本做法
//手动释放资源
try{
可能出现问题的代码
}catch(异常类名 变量名){
异常的处理代码
}finally{
执行所有资源释放操作
}
JDK7方案
//资源用完最终自动释放
try(创建流对象1;创建流对象2){//只有实现AutoCloseable接口的类才能在在此创建对象
可能出现问题的代码
}catch(异常类名 变量名){
异常的处理代码
}
JDK9方案
//资源用完最终自动释放
创建流对象1;
创建流对象2;
try(流1;流2){//只有实现AutoCloseable接口的类才能在在此创建对象
可能出现问题的代码
}catch(异常类名 变量名){
异常的处理代码
}
字符集
ASCII
一个英文占一个字符。
ASCII编码规则:前面补0,补全8位。
ASCII解码规则:直接转换成十进制。
GBK
简体中文版Windows,默认使用GBK字符集。
GBK字符集完全兼容ASCII字符集。
一个英文占一个字节,二进制第一位是0。
一个中文占两个字节,二进制高位字节的第一位是1。
Unicode
Unicode万国码。
Unicode字符集的UTF-8编码格式:
- 一个英文占一个字节,二进制第一位是0,转成十进制是正数
- 一个中文占三个字节,二进制第一位是1,装成十进制数是负数
乱码
产生乱码的原因:
- 读取数据时未读完整个汉字
- 编码和解码时的方式不统一
如何不产生乱码:
- 不要用字节流读取文本文件
- 编码解码时,使用同一个码表,同一个编码方式
编码和解码方法
编码
格式1:
public byte[] getBytes()
说明:使用默认方式编码,IDEA为UTF-8。
格式2:
public byte[] getBytes(String charsetName)
说明:使用指定方式编码。
解码
格式1:
String(byte[] bytes)
说明:使用默认方式解码。
格式2:
String(byte[] bytes,String charsetName)
说明:使用指定方式解码。
范例
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
public class Test{
public static void main(String[] args) throws UnsupportedEncodingException {
String str1="ni是憨批";
//默认utf-8
byte[] bytes1=str1.getBytes();
System.out.println(Arrays.toString(bytes1));
//指定GBK
byte[] bytes2=str1.getBytes("GBK");
System.out.println(Arrays.toString(bytes2));
//bytes1编码方式为utf-8
//以utf-8方式解码bytes1
String strUtf1=new String(bytes1);
System.out.println(strUtf1);
//以gbk方式解码bytes1,结果为乱码
String strGbk1=new String(bytes1,"GBK");
System.out.println(strGbk1);
//bytes2编码方式为GBK
//以utf-8方式解码bytes2,结果为乱码
String strUtf2=new String(bytes2);
System.out.println(strUtf2);
//以gbk方式解码bytes2
String strGbk2=new String(bytes2,"GBK");
System.out.println(strGbk2);
}
}
/*
[110, 105, -26, -104, -81, -26, -122, -88, -26, -119, -71]
[110, 105, -54, -57, -70, -87, -59, -6]
ni是憨批
ni鏄啫鎵�
ni�Ǻ���
ni是憨批
*/
字符流
字符流底层就是字节流,适用于对纯文本文件进行操作。
字符流=字节流+字符集。
输入流:一次读一个字节,遇到中文,一次读取多个字节。
输出流:底层会将数据按照指定的编码方式进行编码,变成字节再写到文件中。
字符输入流Reader
FileReader
步骤:
- 创建字符输入流对象,若文件不存在会报错
- 构造方法:FileReader(File file)
- 创建字符输入流并关联本地文件
- 构造方法:FileReader(String pathname)
- 创建字符输入流关联本地文件
- 构造方法:FileReader(File file)
- 读取数据:按字节进行读取,遇到中文,一次读取多个字节,读取后编码,返回一个整数
- 方法:public int read()
- 读取数据,读到末尾返回-1
- 默认是一个字节一个字节读取,若遇到中文,会一次读取多个
- 读取之后,方法的底层还会进行解码,并转换为十进制,并将该十进制作为返回值
- 方法:public int read(char[] buffer)
- 读取多个数据,读到末尾返回-1
- 返回数组长度
- 空参的read+强制类型转换,无需再手动转换
- 方法:public int read()
- 释放资源
- 成员方法:public int close()
- 说明:释放资源/关闭
范例:
//read()
import java.io.FileReader;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("a.txt");
int c;
while((c=fr.read())!=-1){
System.out.print((char)c);
}
fr.close();
}
}
//read(char[] buffer)
import java.io.FileReader;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("a.txt");
char[] chars=new char[5];
int len;
while((len=fr.read(chars))!=-1){
System.out.print(new String(chars,0,len));
}
fr.close();
}
}
FileWriter
步骤:
- 创建字符输出流对象
- 参数可以是字符串表示的路径或File对象
- 若文件不存在则会创建新文件,但要保证父级路径存在
- 若文件已经存在则会清空文件,不想清空可以打开续写开关,即第二个参数写true
- 构造方法
- public FileWriter(File file) 创建字符输出流并关联本地文件
- public FileWriter(String pathname) 创建字符输出流并关联本地文件
- public FileWriter(File file,boolean append) 创建字符输出流并关联本地文件,续写
- public FileWriter(String pathname,boolean append) 创建字符输出流并关联本地文件,续写
- 写数据:若write方法是整数,写到本地文件的是整数在字符集上对应的字符
- 成员方法
- void write(int c) 写出一个字符
- void write(String str) 写出一个字符串
- void write(String str,int off,int len) 写出一个字符串的一部分
- void write(char[] cbuf) 写出一个字符数组
- void write(char[] cbuf,int off,int len) 写出字符数组的一部分
- 成员方法
- 释放资源:每次用完流后都要释放资源
范例:
import java.io.FileWriter;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileWriter fw1=new FileWriter("a.txt");
fw1.write(25105);
FileWriter fw2=new FileWriter("a.txt",true);
fw1.write("是博主");
fw1.write("abcde",0,2);
char[] chars={'你','是','观','众'};
fw1.write(chars);
fw1.write(chars,0,2);
fw1.close();
}
}
/*a.txt
我是博主ab你是观众你是
*/
字符输入流底层原理
- 创建字符输入流对象,底层关联文件,并创建缓冲区(长度为8192的字节数组)
- 读取数据,
- 判断缓冲区中是否有数据可以读取
- 缓冲区没有数据:从文件获取数据,装到缓冲区,每次尽可能填满缓冲区,若文件也没有数据,返回-1
- 缓冲区有数据:从缓冲区读取数据
- 空参的read方法,一次读取一个字节,遇到中文一次读多个字节,把字节解码并转成十进制返回
- 有参的read方法,把读取字节,解码,强转三步合并,强转之后的字符放到数组中。
字符输出流底层原理
- 创建字符输出流对象,底层关联文件,并创建缓冲区(长度为8192的字节数组)
- 当数据缓冲区,手动刷新(flush)或关流(close)时,会将缓冲区中的数据存储到文件中。
- flush刷新:刷新之后,依旧可向文件写出数据
- close关流:断开通道,不可忘文件写出数据。
范例
例1
//需求:拷贝一个文件夹,考虑子文件夹
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
File src=new File("d:\\javafiletest\\a");
File det=new File("d:\\javafiletest\\copy");
copyFile(src,det);
}
public static void copyFile(File src,File det) throws IOException {
det.mkdirs();
File[] files=src.listFiles();
for(File file:files) {
if (file.isFile()) {
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(new File(det, file.getName()));
byte[] bytes = new byte[1024 * 5];
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fis.close();
fos.close();
} else {
copyFile(file,new File(det,file.getName()) );
}
}
}
}
例2
/*
为保证文件的安全性,需对原始文件进行加密存储,在使用时进行解密
加密原理:
对原始文件的每个字节进行更改,将更改的数据存到新文件中
解密原理:
读取加密后的文件,按加密规则反向操作,变成原始文件
*/
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Test{
public static void main(String[] args) throws IOException {
FileInputStream fis1=new FileInputStream("src.PNG");
FileOutputStream fos1=new FileOutputStream("jiami.PNG");
int c;
while ((c=fis1.read())!=-1){
fos1.write(c^100);
}
fos1.close();
fis1.close();
//一个数字异或另一个数字两次,得到的结果还是数字本身
FileInputStream fis2=new FileInputStream("jiami.PNG");
FileOutputStream fos2=new FileOutputStream("jiemi.PNG");
while((c=fis2.read())!=-1){
fos2.write(c^100);
}
fos2.close();
fis2.close();
}
}
例3
/*
文本文件中有以下的数据:
6-8-4-2-1
将文件中的数据进行排序,变成以下数据
1-2-4-6-8
*/
//方式1
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.StringJoiner;
public class Test{
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("src.txt");
StringBuilder sb=new StringBuilder();
int c;
while((c=fis.read())!=-1){
sb.append((char)c);
}
String[] s=sb.toString().split("-");
int[] arr=new int[s.length];
for (int i=0;i<arr.length;i++){
arr[i]=Integer.parseInt(s[i]);
}
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
StringJoiner sj=new StringJoiner("-");
for(int a:arr){
sj.add(""+a);
}
FileOutputStream fos=new FileOutputStream("src.txt");
byte[] bytes=sj.toString().getBytes();
fos.write(bytes);
fos.close();
fis.close();
}
}
//方式2
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
public class Test{
public static void main(String[] args) throws IOException {
FileReader fis=new FileReader("src.txt");
StringBuilder sb=new StringBuilder();
int c;
while((c=fis.read())!=-1){
sb.append((char)c);
}
String[] sarr=sb.toString().split("-");
Integer[] iarr=Arrays.stream(sarr).
map(Integer::parseInt).
sorted().
toArray(Integer[]::new);
System.out.println(Arrays.toString(iarr));
String s=Arrays.toString(iarr).
replace(", ","-");
String str=s.substring(1,s.length()-1);
FileWriter fos=new FileWriter("src.txt");
fos.write(str);
fos.close();
fis.close();
}
}
缓冲流
原理:底层自带长度为8192的缓冲区提高性能,字节缓冲流8192个byte,字符缓冲流8192个char。
- 字节缓冲流,字节流本身并无缓冲区,但可加入缓冲流加快效率,即字节缓冲流
- BufferedInputStream:字节缓冲输入流
- BufferedOutputStream:字节缓冲输出流
- 字符缓冲流,字符缓冲流对效率提升不大,但字符缓冲流中特有方法比较重要
- BufferedReader:字符缓冲输入流
- BufferedWriter:字符缓冲输出流
字节缓冲流
构造方法
BufferedInputStream
格式:
public BufferedInputStream(InputStream is)
说明:把基本流包装成高级流,提高读取数据的性能
BufferedOutputSream
格式:
public BufferedOutputStream(OutputStream os)
说明:把基本流包装成高级流,提高写出数据的性能
范例
/*
需求:
利用字节缓冲流拷贝文件
*/
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
BufferedInputStream bis=new BufferedInputStream(new FileInputStream("src.txt"));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream("copy.txt"));
byte[] bytes=new byte[1024];
int len;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.close();
bis.close();
}
}
字符缓冲流
构造方法
BufferedReader
格式:
public BufferedReader(Reader r)
说明:把基本流变成高级流
BufferedWriter
格式:
public BufferedWriter(Writer r)
说明:把基本流变成高级流
特有方法
readLine
格式:
public String readLine()
说明:读取一行数据,遇到回车换行结束,读取的数据不包括回车换行,若没有数据可读,返回null
newline
格式:
public void newLine()
说明:跨平台的换行
范例
/*src.txt内容
君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。
古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。
*/
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("src.txt"));
BufferedWriter bw=new BufferedWriter(new FileWriter("src.txt",true));
String s;
while((s=br.readLine())!=null){
System.out.println(s);
}
bw.newLine();
bw.write("主人何为言少钱,径须沽取对君酌。");
bw.newLine();
bw.write("五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。");
bw.close();
br.close();
}
}
/*打印
君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。
古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。
*/
/*src.txt内容
君不见黄河之水天上来,奔流到海不复回。
君不见高堂明镜悲白发,朝如青丝暮成雪。
人生得意须尽欢,莫使金樽空对月。
天生我材必有用,千金散尽还复来。
烹羊宰牛且为乐,会须一饮三百杯。
岑夫子,丹丘生,将进酒,杯莫停。
与君歌一曲,请君为我倾耳听。
钟鼓馔玉不足贵,但愿长醉不复醒。
古来圣贤皆寂寞,惟有饮者留其名。
陈王昔时宴平乐,斗酒十千恣欢谑。
主人何为言少钱,径须沽取对君酌。
五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
*/
范例
例1
//四种方式拷贝文件,并统计各自用时
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
method1();
method2();
method3();
method4();
}
public static void method1() throws IOException {
System.out.println("方式1:字节流的基本流:一次读写一个字节");
FileInputStream fis1=new FileInputStream("a.mp4");
FileOutputStream fos1=new FileOutputStream("copy1.mp4");
int c;
long start=System.currentTimeMillis();
System.out.println("start:"+start);
while((c=fis1.read())!=-1){
fos1.write(c);
}
long end=System.currentTimeMillis();
System.out.println("end:"+end);
fos1.close();
fis1.close();
System.out.println((end-start)/1000.0+"秒");
}
public static void method2() throws IOException {
System.out.println("方式2:字节流的基本流,一次读写一个字节数组");
FileInputStream fis2=new FileInputStream("a.mp4");
FileOutputStream fos2=new FileOutputStream("copy2.mp4");
byte[] bytes2=new byte[1024*1024*10];
long start=System.currentTimeMillis();
System.out.println("start:"+start);
int len;
while((len=fis2.read(bytes2))!=-1){
fos2.write(bytes2,0,len);
}
long end=System.currentTimeMillis();
System.out.println("end:"+end);
fos2.close();
fis2.close();
System.out.println((end-start)/1000.0+"秒");
}
public static void method3() throws IOException {
System.out.println("方式3:字节缓冲流:一次读写一个字节");
BufferedInputStream bis1=new BufferedInputStream(new FileInputStream("a.mp4"));
BufferedOutputStream bos1=new BufferedOutputStream(new FileOutputStream("copy3.mp4"));
int bc;
long start=System.currentTimeMillis();
System.out.println("start:"+start);
while((bc=bis1.read())!=-1){
bos1.write(bc);
}
long end=System.currentTimeMillis();
System.out.println("end:"+end);
bos1.close();
bis1.close();
System.out.println((end-start)/1000.0+"秒");
}
public static void method4() throws IOException {
System.out.println("方式4:字节缓冲流,一次读写一个字节数组");
BufferedInputStream bis2=new BufferedInputStream(new FileInputStream("a.mp4"));
BufferedOutputStream bos2=new BufferedOutputStream(new FileOutputStream("copy4.mp4"));
int blen;
byte[] bbytes=new byte[1024*1024*10];
long start=System.currentTimeMillis();
System.out.println("start:"+start);
while((blen=bis2.read(bbytes))!=-1){
bos2.write(bbytes,0,blen);
}
long end=System.currentTimeMillis();
System.out.println("end:"+end);
bos2.close();
bis2.close();
System.out.println((end-start)/1000.0+"秒");
}
}
/*
方式1:字节流的基本流:一次读写一个字节
start:1680080313285
end:1680081118655
805.37秒
方式2:字节流的基本流,一次读写一个字节数组
start:1680081118661
end:1680081118804
0.143秒
方式3:字节缓冲流:一次读写一个字节
start:1680081118806
end:1680081119696
0.89秒
方式4:字节缓冲流,一次读写一个字节数组
start:1680081119701
end:1680081119846
0.145秒
*/
例2
//给《将进酒》的文章顺序进行恢复到一个新文件夹中
/*src.txt
2.君不见高堂明镜悲白发,朝如青丝暮成雪。
3.人生得意须尽欢,莫使金樽空对月。
1.君不见黄河之水天上来,奔流到海不复回。
12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
4.天生我材必有用,千金散尽还复来。
5.烹羊宰牛且为乐,会须一饮三百杯。
7.与君歌一曲,请君为我倾耳听。
10.陈王昔时宴平乐,斗酒十千恣欢谑。
11.主人何为言少钱,径须沽取对君酌。
9.古来圣贤皆寂寞,惟有饮者留其名。
6.岑夫子,丹丘生,将进酒,杯莫停。
8.钟鼓馔玉不足贵,但愿长醉不复醒。
*/
import java.io.*;
import java.util.TreeSet;
public class Test{
public static void main(String[] args) throws IOException {
BufferedReader bis=new BufferedReader(new FileReader("src.txt"));
BufferedWriter bos=new BufferedWriter(new FileWriter("det.txt"));
TreeSet<String> ts=new TreeSet<>((o1,o2) ->Integer.parseInt(o1.split("\\.")[0])-Integer.parseInt(o2.split("\\.")[0]));
String s;
while((s=bis.readLine())!=null){
ts.add(s);
}
for(String str:ts){
bos.write(str);
bos.newLine();
}
bos.close();
bis.close();
}
}
/*det文件
1.君不见黄河之水天上来,奔流到海不复回。
2.君不见高堂明镜悲白发,朝如青丝暮成雪。
3.人生得意须尽欢,莫使金樽空对月。
4.天生我材必有用,千金散尽还复来。
5.烹羊宰牛且为乐,会须一饮三百杯。
6.岑夫子,丹丘生,将进酒,杯莫停。
7.与君歌一曲,请君为我倾耳听。
8.钟鼓馔玉不足贵,但愿长醉不复醒。
9.古来圣贤皆寂寞,惟有饮者留其名。
10.陈王昔时宴平乐,斗酒十千恣欢谑。
11.主人何为言少钱,径须沽取对君酌。
12.五花马、千金裘,呼儿将出换美酒,与尔同销万古愁。
*/
例3
/*实现一个验证程序运行次数的小程序,要求如下:
1.当程序运行次数超过3次,给出提示:运行次数超过3次,请充值会员后使用
2.程序运行演示如下:
第一次:第1次运行此文件
第二次:第2次运行此文件
第三次:第3次运行此文件
第四次及以后:运行次数超过3次,请充值会员后使用
*/
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
BufferedReader br=new BufferedReader(new FileReader("src.txt"));
int c=Integer.parseInt(br.readLine());
c++;
if(c<=3){
System.out.println("第"+c+"次运行此文件");
}else{
System.out.println("运行次数超过3次,请充值会员后使用");
}
BufferedWriter bw=new BufferedWriter(new FileWriter("src.txt"));
bw.write(c+"");
bw.close();
br.close();
}
}
转换流
转换流是字符流和字节流之间的桥梁。
作用:
- 指定字符集读写(在jdk11被淘汰)
- 字节流想要使用字符流中的方法
InputStreamReader
将字节流转换成字符流,可以根据字符集一次读取多个字节,读取数据不会乱码
OutputStreamWriter
将字符流转换成字节流。
范例
例1
//利用转换流按照指定字符编码读取
//jdk8
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK");
int ch;
while((ch=isr.read())!=-1){
System.out.print((char)ch);
}
isr.close();
}
}
//jdk11
import java.io.*;
import java.nio.charset.Charset;
public class Test{
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("src.txt",Charset.forName("GBK"));
int ch;
while((ch=fr.read())!=-1){
System.out.print((char)ch);
}
fr.close();
}
}
例2
//利用转换流按照指定字符编码写出
//jdk8
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
OutputStreamWriter osr=new OutputStreamWriter(new FileOutputStream("src.txt"),"GBK");
osr.write("你好!");
osr.close();
}
}
//jdk11
import java.io.FileWriter;
import java.io.IOException;
import java.nio.charset.Charset;
public class Test{
public static void main(String[] args) throws IOException {
FileWriter fw=new FileWriter("src.txt", Charset.forName("GBK"));
fw.write("你好啊");
fw.close();
}
}
例3
//将本地文件的GBK文件,转成UTF-8文件
//jdk8
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK");
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("det.txt"),"UTF-8");
int ch;
while((ch=isr.read())!=-1){
osw.write(ch);
}
osw.close();
isr.close();
}
}
//jdk11
import java.io.*;
import java.nio.charset.Charset;
public class Test{
public static void main(String[] args) throws IOException {
FileReader fr=new FileReader("src.txt", Charset.forName("GBK"));
FileWriter fw=new FileWriter("det.txt",Charset.forName("UTF-8"));
int ch;
while((ch=fr.read())!=-1){
fw.write(ch);
}
fw.close();
fr.close();
}
}
例4
//利用字节流读取文件中的数据,每次读一整行,而且不能出现乱码
import java.io.*;
public class Test{
public static void main(String[] args) throws IOException {
InputStreamReader isr=new InputStreamReader(new FileInputStream("src.txt"),"GBK");
BufferedReader br=new BufferedReader(isr);
String s;
while((s=br.readLine())!=null){
System.out.println(s);
}
isr.close();
}
}
序列化流和反序列化流
细节:
- 使用序列化流将对象写到文件时,需使javabean类实现Serializable接口,否则就会出现NotSerializableException异常
- 序列化流写到文件中的数据不可修改,一旦修改就无法再读回来
- 序列化对象后,修改Javabean类,再次序列化,便会抛出InvalidClassException异常
- 解决方案:可以通过给javabean类添加serialVersionUID(版本号,序列号)
- 若对象中的某成员变量不想被序列化,则给该变量添加transient关键字修饰,该关键字表示的成员变量不参与序列化过程。
序列化流
又称对象操作输出流,可以把java中的对象写到本地文件中。
对象需要实现Serializable接口,写到文件中的内容是看不懂的。
Serializable接口里面没有抽象方法,标记性接口。
实现了该接口,就表示当前的类可以被序列化。
构造方法
public ObjectOutputStream(OutputStream out)
说明:把基本流包装成高级流
成员方法
public final void writeObject(Object obj)
说明:把对象序列化(写出)到文件夹中去
范例
//Student.java
import java.io.Serializable;
public class Student implements Serializable {
private int age;
private String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Student{age = " + age + ", name = " + name + "}";
}
}
//Test.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Test{
public static void main(String[] args) throws IOException {
Student student=new Student(19,"张三");
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("src.txt"));
oos.writeObject(student);
oos.close();
}
}
反序列流
又称对象操作输入流,可以把序列化到本地文件中的对象,读取到程序中来。
构造方法
public ObjectInputStream(OutputStream out)
说明:把基本流包装成高级流
成员方法
public final Object readObject()
说明:把序列化到本地文件中的对象,读取到程序中来。
范例
//Student.java
import java.io.Serializable;
public class Student implements Serializable {
private int age;
private String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Student{age = " + age + ", name = " + name + "}";
}
}
//Test.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class Test{
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream oim=new ObjectInputStream(new FileInputStream("src.txt"));
Student student=(Student)oim.readObject();
System.out.println(student);
}
}
范例
//将多个自定义对象序列化到文件中,由于对象个数不确定,反序列化该如何读取?
//Student.java
import java.io.Serializable;
public class Student implements Serializable{
private static final long serialVersionUID = -4683704568207367494L;
private int age;
private String name;
public Student() {
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
public String toString() {
return "Student{age = " + age + ", name = " + name + "}";
}
}
//Test.java
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
public class Test{
public static void main(String[] args) throws IOException, ClassNotFoundException {
ArrayList<Student> list=new ArrayList<>();
Student s1=new Student(19,"张三");
Student s2=new Student(18,"李四");
Student s3=new Student(20,"王五");
list.add(s1);
list.add(s2);
list.add(s3);
ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("src.txt"));
oos.writeObject(list);
oos.close();
}
}
//Test1.java
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
public class Test1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectInputStream ois=new ObjectInputStream(new FileInputStream("src.txt"));
ArrayList<Student> list=(ArrayList<Student>) ois.readObject();
for (Student student:list){
System.out.println(student);
}
ois.close();
}
}
/*
Student{age = 19, name = 张三}
Student{age = 18, name = 李四}
Student{age = 20, name = 王五}
*/
打印流
打印流一般是指:PrintStream,PrintWriter两个类。
特点:
- 打印流只操作文件目的地,不操作数据源
- 特有的写出方法可以实现,数据原样打出
- 特有的写出方法。可以实现自动刷新。自动换行,打印一次数据=写出+换行+刷新
注:字节流底层没有缓冲区,开不开自动刷新都一样
字节打印流
构造方法
格式1:
public PrintStream(OutputStream/File/String)
说明:关联字节输出流/文件/文件路径
格式2:
public PrintStream(String fileName,Charset charset)
说明:指定字节编码
格式3:
public PrintStream(OutputStream out,boolean autoFlush)
说明:自动刷新
格式4:
public PrintStream(OutputStream out,boolean autoFlush,String encoding)
说明:指定字符编码且自动刷新
成员方法
write
格式:
public void write(int b)
说明:常规方法,规则跟之前一样,将指定的字节写出。
println
格式:
public void println(Xxx xx)
说明:特有方法,打印任意数据,自动刷新,自动换行,数据原样写出
格式:
public void print(Xxx xx)
说明:特有方法,打印任意数据,不换行,数据原样写出
printf
格式:
public void printf(String format,Object…args)
说明:特有方法,带有占位符的打印语句,不换行,数据原样写出
范例
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
public class Test{
public static void main(String[] args) throws FileNotFoundException, UnsupportedEncodingException {
PrintStream ps=new PrintStream(new FileOutputStream("src.txt"),true,"GBK");
ps.println(97);
ps.print(true);
ps.println();
ps.printf("学生姓名:%s,年龄%d","张三",18);
ps.close();
}
}
/*src.txt内容
97
true
学生姓名:张三,年龄18
*/
字符打印流
字符流底层有缓冲区,要自动刷新需要开启。
构造方法
格式1:
public PrintWriter(Write/File/String)
说明:关联字符输出流/文件/文件路径
格式2:
public PrintWriter(String fileName,Charset charset)
说明:指定字符编码
格式3:
public PrintWriter(Write out,boolean autoFlush)
说明:自动刷新
格式4:
public PrintWriter(OutputStream,boolean autoFlush,String encoding)
说明:指定字符编码且自动刷新
成员方法
write
格式:
public void write(int b)
说明:常规方法,规则跟之前一样,写出字节或字符串。
println
格式:
public void println(Xxx xx)
说明:特有方法,打印任意数据且换行,数据原样写出
格式:
public void print(Xxx xx)
说明:特有方法,打印任意数据,不换行,数据原样写出
printf
格式:
public void printf(String format,Object…args)
说明:特有方法,带有占位符的打印语句,不换行,数据原样写出
范例
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
public class Test{
public static void main(String[] args) throws IOException {
PrintWriter pw=new PrintWriter(new FileWriter("src.txt"),true);
pw.println(97);
pw.print(true);
pw.println();
pw.printf("学生姓名:%s,年龄%d","张三",18);
pw.close();
}
}
压缩和解压缩
压缩包里的每一个文件。在Java中都是一个ZipEntry对象。
在java中只能识别zip格式的压缩包
解压缩
解压本质:把每一个ZipEntry按照层级拷贝到本地另一个文件夹中。
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class Test{
public static void main(String[] args) throws IOException {
//创建一个File表示要解压的文件
File src=new File("d:\\javafiletest.zip");
//创建一个File表示解压的目的地
File det=new File("d:\\");
unzip(src,det);
}
public static void unzip(File src,File det) throws IOException {
//创建一个解压缩流用来读取压缩包中的数据
ZipInputStream zip=new ZipInputStream(new FileInputStream(src));
//要先获取到压缩包里的每个zipEntry对象
//表示当前在压缩包中获取到的文件或文件夹
ZipEntry entry;
while((entry=zip.getNextEntry())!=null){
System.out.println(entry);
if(entry.isDirectory()){
//若entry是文件夹,则创建对应的文件夹
File file=new File(det,entry.toString());
file.mkdirs();
}else {
//entry是文件,读取该文件,并将其放到指定位置
FileOutputStream fos=new FileOutputStream(new File(det,entry.toString()));
int ch;
while ((ch=zip.read())!=-1){
//写到目的地
fos.write((char)ch);
}
fos.close();
//表示在压缩包中一个文件处理完毕
zip.closeEntry();
}
}
zip.close();
}
}
压缩
压缩本质:把每一个(文件/文件夹)看成ZipEntry对象放到压缩包中。
//压缩单个文件
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Test{
public static void main(String[] args) throws IOException {
//创建一个File表示要压缩的文件
File src=new File("d:\\javafiletest\\b.txt");
//创建一个File表示压缩包的位置
File det=new File("d:\\javafiletest\\b.zip");
zip(src,det);
}
public static void zip(File src,File det) throws IOException {
//创建关联压缩包
ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(det));
//创建ZipEntry对象,表示压缩包里面的每个文件和文件夹
ZipEntry zipEntry=new ZipEntry("b.txt");
//把ZipEntry对象放到压缩包中
zos.putNextEntry(zipEntry);
//把src文件的数据写到压缩包中
FileInputStream fis=new FileInputStream(src);
int c;
while((c=fis.read())!=-1){
zos.write(c);
}
fis.close();
zos.closeEntry();
zos.close();
}
}
//压缩文件夹
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Test{
public static void main(String[] args) throws IOException {
File src=new File("d:\\javafiletest");
File destParent=src.getParentFile();
File det=new File(destParent,src.getName()+".zip");
ZipOutputStream zos=new ZipOutputStream(new FileOutputStream(det));
directoryZip(src,zos,src.getName());
zos.close();
}
public static void directoryZip(File src,ZipOutputStream zos,String name) throws IOException {
File[] files=src.listFiles();
for(File file:files){
if(file.isFile()){
ZipEntry zipEntry=new ZipEntry(name+"\\"+file.getName());
zos.putNextEntry(zipEntry);
FileInputStream fis=new FileInputStream(file);
int c;
while ((c=fis.read())!=-1){
zos.write(c);
}
fis.close();
zos.closeEntry();
}else {
directoryZip(file,zos,name+"\\"+file.getName());
}
}
}
}
Common-io
Common-io是apache开源基金组织提供的一组有关IO操作的开源工具包。
作用:提高IO流的开发效率
使用步骤
IDEA下的使用步骤:
- 在项目中创建文件lib
- 将jar包复制粘贴到lib文件夹
- 右键点击jar包,选择Add as Library->点击OK
- 在类中导包使用
常见方法
FileUtils类
FileUtils类,文件/文件夹相关。
copyFile
static void copyFile(File srcFile,File destFile)
说明:复制文件
copyDirectory
static void copyDirectory(File srcDir,File destDir)
说明:复制文件夹
copDirectoryToDirectory
static void copDirectoryToDirectory(File srcDir,File destDir)
说明:复制文件夹,包括最顶层的文件夹
deleteDirectory
static void deleteDirectory(File Directory)
说明:删除文件夹
clearDirectory
static void clearDirectory()
说明:清空文件夹
IOUtils类
IOUtils类(流相关相关)
copy
public static int copy(InputStream input,OutStream output)
说明:复制文件
copyLarge
public static int copyLarge(Reader input,Writer output)
说明:复制大文件
readlines
public static String readlines(Reader input)
说明:读取数据
write
public static void write(String data,OutputStream output)
说明:写出数据
hutool
常见方法
FileUtil类
file
说明:根据参数创建一个File对象,参数可以是多个字符串
touch
说明:根据参数创建对象,若父级路径不存在,则会自动创建父级路径
writeLines
说明:把集合中的数据写出到文件中,覆盖模式
appendLines
说明:把集合中的数据写出到文件中,续写模式
readLines
说明:指定字符编码,把文件中的数据,读到集合中
readUtf8Lines
说明:按照Utf-8的形式,把文件中的数据,读到集合中
copy
说明:拷贝文件或文件夹