目录
1、使用FileInputStream和FileOutputStream来实现文件的拷贝
2、使用FileReader和FileWriter对文本文件进行复制
一、I/O概述
1、什么是IO
生活中,你肯定经历过这样的场景,当你编辑一个文本文件,忘记了ctrl+s,可能文件就白白编辑了。当你电脑插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里,那么数据都是在那些设备上呢?键盘、内存、硬盘、外设等等。我们把这种数据的传输,可以看作是一种数据的流动,按照流动的方向,以内存为基准,分为输入input和输出output,即流向内存是输入流,流出内存是输出流。
Java中io操作主要是指用java.io包下的内容,进行输入、输出操作,输入也叫做读取数据,输出也叫做写出数据。Java中所有的流都在java.io.*包下。
2、 IO的分类
2.1、根据数据的流向分为:输入流和输出流
输入流:把数据从其他设备上读取到内存中的流。
输出流:把数据从内存中写出到其他设备上的流。
2.2、根据数据的类型分为:字节流和字符流。
字节流:以字节(byte)为单位,读写数据的流
字符流:以字符(char)为单位,读写数据的流
注意:在Java中只要"类名"以“Stream”结尾都是字节流,以“Reader/Writer”结尾的都是字符流
3、 顶级父类
| 输入流 | 输出流 |
字节流 | 字节输入流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
注意:
1、这四个类均为抽象类
2、这四个类都实现了Java.io.Closeable接口,所有的流都是可关闭的,都有close()方法,流毕竟是一个管道,这个是内存与硬盘之间的通道,用完之后一定晏关闭,不然会浪费很多资源。
3、所有的输出流都实现了java.io.Flashable接口。都是可刷新的,都有flash()方法。输出流在最终输出后,一定要记得flash()方法,这个刷新表示将管道/通道当中剩余未输出的数据强行输出完,刷新的作用就是清空管道!如果没有flash()可能会丢失数据。
二、字节流
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都是一个一个的字节,那么传输时一样如此。所以,字节流可以传入任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
注意:
字节流是一个一个字节读取的,字母占一个字节,中文占两个字节,所以读中文时一次只能读该字符的一半
1 、字节输出流OutputStream
Java.io.OutputStream 抽象类是表示字节输出流的所有的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
Public void close();关闭此输出流并释放与此流相关联的任何系统资源。
Public void flush();刷新此输出流并强制任何缓冲流的输出字节被写出。
Public void write(byte[] b);将b.length字节从指定的字节数组写入此输出流
Public void write(byte[] b,int off,int len);从指定的字节数组写入len字节,从偏移量off开始输出到此输出流
Public void write(int b);将指定的字节输出流
2、 FileOutputStream类(文件专属)
OutputStream有很多子类,java.io. FileOutputStream类是文件输出流,用于将数据写出到文件
2.1、创建流对象
当你创建一个流对象时,必须传入一个文件路径,该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。再写入。代码如下:
public class FileOutputStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
//使用File对象创建流对象
File file = new File("d:\\a.txt");
FileOutputStream fos1 = new FileOutputStream(file);
//使用文件名称创建流对象
FileOutputStream fos2 = new FileOutputStream("d:\\b.txt");
}
}
2.2、写出字节数据:
2.2.1、写出字节:write(int b)方法 每次可以写出一个字节数据,代码使用演示:
public class FOSWrite {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileOutputStream fos = new FileOutputStream("d:\\a.txt");
//写出数据
fos.write(97);
fos.write(98);
fos.write(99);
//关闭资源
fos.close();
}
}
2.2.2、写出字节数组:write(byte[] b) 每次可以写出数组中的数据,使用代码演示:
public class FOSWrite {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileOutputStream fos = new FileOutputStream("d:\\a.txt");
byte[] b = "Good Good Study".getBytes();
//写出字节数组数据
fos.write(b);
//关闭资源
fos.close();
}
}
2.2.3、写出指定长度字节数组: write(byte[] b,int off,int len)每次写出从off索引开始,len个字节,代码使用演示
public class FOSWrite {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileOutputStream fos = new FileOutputStream("d:\\a.txt");
byte[] b = "day day up".getBytes();
//写出字节数组数据
fos.write(b,2,2);
//关闭资源
fos.close();
}
}
备注:写出的是y+空格
2.3数据追加续写
经过上面的演示,每次程序运行,创建出流对象,就会清空目标文件中的数据,如何保留目标文件中的数据,还能继续添加新数据吗?
Public FileOutputStream(File file,Boolean append);创建文件输出流以写入由指定的File对象表示的文件。
Public FileOutputStream(String name,booean append):创建文件输出流以指定的名称写入文件。
这两个构造方法,参数中都需要传入一个Boolean类型的值,true表示追加数据,false表示清空原有数据据,这样创建的输出流对象,就可以指定是否追加续写了,
【代码演示】
public class FOSWrite {
public static void main(String[] args) throws IOException {
//创建流对象
FileOutputStream fos = new FileOutputStream("D:\\a.txt", true);
//字符串转换为字节数组
byte[] b = "hello".getBytes();
//写数据
fos.write(b);
//关闭资源
fos.close();
}
}
2.4写出换行
Windows系统里,换行符号是\r\n。把\r\n以指定是否追加续写,代码使用演示:
public class FOSWrite {
public static void main(String[] args) throws IOException {
//创建流对象
FileOutputStream fos = new FileOutputStream("D:\\a.txt");
//字符串转换为字节数组
byte[] b = {97,98,99,100,101};
//遍历数组
for(int i=0;i<b.length;i++){
//写出一个字节
fos.write(b[i]);
//写出一个换行,换行符号转成数组写出
fos.write("\r\n".getBytes());
}
//关闭资源
fos.close();
}
}
注意:
回车符 \r 和换行符\n“
回车符:回到一行的开头(return)
换行符:下一行(newline)
系统中的换行:
Windows系统里,每行结尾都是回车+换行,即\r\n
Linux系统里,每行结尾之后换行,即\n
Mac系统,每行结尾都是回车,即\r。从Mac OS X开始与Linux统一。
3、 字节输入流InputStream
Java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
Public void close();关闭此输入流并释放与此流相关的任何系统资源
Public abstract int read();从输入流读取数据的下一个字节
Public int read(byte[] b):从输入流中读取一些字节数,并将他们存储到字节数组b中
4、FileInputStream类(文件专属)
Java.io.FileInputStream 类是文件输入流,从文件中读取字节。
4.1构造方法:
FileInputStream(File file):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的File对象file命名。
FileInputStream(String name);通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name 命名。当你创建一个流对象时,必须传入一个文件路径,该路径下,如果没有该文件,会抛出FileNotFoundException。代码如下:
public class FlieInputStreamDemo {
public static void main(String[] args) throws FileNotFoundException {
//使用File对象创建流对象(字节输入流)
File file = new File("d:\\a.txt");
FileInputStream fos1 = new FileInputStream(file);
//使用文件名来创建流对象
FileInputStream fos2 = new FileInputStream("d:\\a.txt");
}
}
4.2读取字节数据
4.2.1、读取字节:read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1,
【代码演示】
public class FISRead {
public static void main(String[] args) throws IOException {
//使用文件名来创建流对象
FileInputStream fis = new FileInputStream("d:\\a.txt");
//读取数据,返回一个字节
int b;
while ((b=fis.read())!=-1){
System.out.println((char)b);
}
//关闭资源
fis.close();
}
}
4.2.2、使用字节数组读取:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1
【代码演示】
public class FISRead {
public static void main(String[] args) throws IOException {
//使用文件名来创建流对象
FileInputStream fis = new FileInputStream("d:\\a.txt");
//定义变量,作为有效个数
int len;
//定义字节数组,作为装字节数组的容器
byte[] b = new byte[2];
//循环读取
while ((len=fis.read(b))!=-1){
//每次读取后,把数组变成字符串打印
System.out.println(new String(b));
}
//关闭资源
fis.close();
}
}
错误修正:
a.txt中的数据为“11 22 33 a”,此时多出来一个3是由于最后一次读取时,只读取一个字节a,数组中,上次读取的数据没有被完全替换,所以要通过len,获取有效的字节
代码使用演示:
public class FISRead {
public static void main(String[] args) throws IOException {
//使用文件名来创建流对象
FileInputStream fis = new FileInputStream("d:\\a.txt");
//定义变量,作为有效个数
int len;
//定义字节数组,作为装字节数组的容器
byte[] b = new byte[2];
//循环读取
while ((len=fis.read(b))!=-1){
//每次读取后,把数组变成字符串打印
System.out.println(new String(b,0,len));
}
//关闭资源
fis.close();
}
}
4.3、 FileInputStream类的其他常用方法
4.3.1、int available():返回流中剩余的没有读到的字节数量
import java.io.FileInputStream;
import java.io.IOException;
public class myclass {
public static void main(String[] args) throws IOException {
FileInputStream file=new FileInputStream("F:\\abc.txt"); //ab cdef
System.out.println(file);
//读取一个字节
int by =file.read();
System.out.println(by); //97
//还剩几个字节?
int a=file.available(); //6
System.out.println("还剩下的字节数长度为:"+a);
}
}
int available()的用途:将byte数组的数组长度指定为总字节数量,可以一次读取文件中的所有字符。代码演示如下所示:
import java.io.FileInputStream;
import java.io.IOException;
public class myclass {
public static void main(String[] args) throws IOException {
FileInputStream file=new FileInputStream("F:\\abc.txt"); //ab cdef
System.out.println(file);
//总共有多少个字节
int a=file.available(); //7
//将byte数组的数组长度指定为总字节数量
byte[] byt1=new byte[a];
//读的次数
int count=file.read(byt1);
System.out.println(count); //7
System.out.println(new String(byt1)); //ab cdef
long b=file.skip(2);
}
}
注意:这种方式不太适合大文件,因为byte数组不能太大
4.3.2、long skip(long n):跳过几个字节不读
import java.io.FileInputStream;
import java.io.IOException;
public class myclass {
public static void main(String[] args) throws IOException {
FileInputStream file=new FileInputStream("F:\\abc.txt"); //ab cdef
System.out.println(file.read());//读取第一个字符 97
file.skip(2); //跳过两个字符
System.out.println(file.read());//再一次读取 99
}
}
三、字符流
字节流虽然什么类型的数据都可以读取,但是在处理文本文件时,可能会遇到一些小问题,比如遇到中文字符,中文字符占两个字节,而字节流只能一个字节一个字节的读取,所以一次不能完整的读取整个中文字符。而字符流正好解决了这一问题,一次读取一个字符,字符流专门用于处理文本文件,不能读取图片、声音、视频等文件。
1、 字符输入流Reader
Java.io.Reader 抽象类是表示用于读写字符流的所有类的超类,可以读取字符信息到内存中,它定义了字符输入流的基本共性功能方法。
Public void close();关闭此流并释放与此流相关的任何系统资源
Public int read();从输入流读取一个字符
Public int read(char[] cbuf);从输入流中读取一些字符,并将他们存储到字符数组cbuf中。
2、 FileReader类(文本文件专属)
Java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。
注意:
- 字符编码:字节与字符对应的规则。Windows系统的中文编码默认的GBK编码表
Idea中UTF-8
2.字节缓冲区:一个字节数组,用来临时存储字节数据。
2.1、构造方法:
FileReader(File file);创建一个新的FileReader,给定要读取的File对象
FileReader(String fileName);创建一个新的FileReader,给定要读取的文件的名称
2.2、读取字符数据
2.2.1、读取字符:read方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取
public class FRRread {
public static void main(String[] args) throws IOException {
//使用文件名称创建流对象
FileReader fr = new FileReader("d:\\a.txt");//112233a
//定义变量 保存数据
int b;
//循环读取
while ((b=fr.read())!=-1){
System.out.println((char) b);
}
//关闭资源
fr.close();
}
}
2.2.2使用字符数组读取:read(char[] cbuf),每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1
public class FRRread {
public static void main(String[] args) throws IOException {
//使用文件名称创建流对象
FileReader fr = new FileReader("d:\\a.txt");//112233a
//定义变量 保存有效字符个数
int len;
//定义字符数组,作为装字符数据的容器
char[] cbuf =new char[3];
//循环读取
while ((len=fr.read(cbuf))!=-1){
System.out.println(new String(cbuf));
}
//关闭资源
fr.close();
}
}
获取有效的字符改进,代码使用演示:
public class FRRread {
public static void main(String[] args) throws IOException {
//使用文件名称创建流对象
FileReader fr = new FileReader("d:\\a.txt");//112233a
//定义变量 保存有效字符个数
int len;
//定义字符数组,作为装字符数据的容器
char[] cbuf =new char[3];
//循环读取
while ((len=fr.read(cbuf))!=-1){
System.out.println(new String(cbuf,0,len));
}
//关闭资源
fr.close();
}
}
3、字符输出流Writer
Java.io.Writer抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地,它定义了字符输入流的基本共性功能方法。
Void write(int c);写入单个字符
Void write (char[] cbuf);写入字符数组
Abstract void write(char[] dbuf,int off,int len);写入字符数组的某一部分,off数组的开始索引,len写出的字符个数
Void write(String str);写入字符串
Void write(String str,int off,int len);写入字符串的某一部分,off字符串的开始索引,len写出字符的个数。
Void flush();刷新该流的缓冲,
Void close();关闭流,但要先刷新它
4、FileWriter类(文本文件专属)
Java.io.FileWriter 类是写出字符到文件的便利类,构造时使用系统默认的字符编码和默认的字节缓冲区
4.1、构造方法:
FileWriter(File file);创建一个新的FileWriter,给定要写入的file对象
FileWriter(String fileName);创建一个新的FileWriter,给定要写入的文件的名称
public class FileWriterDemo {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileWriter fw1 = new FileWriter("D:\\a.txt");
//使用file对象创建流对象
File file = new File("D:\\a.txt");
FileWriter fw2 = new FileWriter(file);
}
}
4.2、基本写出数据
写出字符:write(int b)方法,每次可以写出一个字符数据,代码使用演示:
public class FWrite {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileWriter fw = new FileWriter("d:\\a.txt");
//写出数据
fw.write(97); //写出第一个字符
fw.write('b');
fw.write('c');
fw.write(30000); //中文编码表中的30000对应一个汉字
fw.close();
}
}
注意:关闭资源是,与FileOutputStream不同,如果不关闭,数据只是保存到缓冲区,并为保存到文件。
关闭和刷新:
因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中,但是关闭的流对象,是无法继续写出的。如果我们既想写出数据,又想继续使用流,就需要使用flush方法了
Flush:刷新缓冲区,流对象可以继续使用。
Close:先刷新缓冲区,然后通知系统释放资源,流对象不可以再被使用了。
代码使用演示:
public class FWrite {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileWriter fw = new FileWriter("d:\\a.txt");
//写出数据
fw.write('刷'); //写出第一个字符
fw.flush();
fw.write('新');
fw.flush();
fw.write('关');
fw.flush();
fw.close();
//fw.write('闭'); //报错 java.io.IOException: Stream closed
//fw.close();
}
}
4.3、写出其他数据
4.3.1、写出字符数组,write(char[] cbuf)和write(char[] cbuf,int off,int len),每次可以写出字符数组中的数据,用法类似FileOutputStream 代码使用演示:
public class FWrite {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileWriter fw = new FileWriter("d:\\a.txt");
//字符串转换为字符数组
char[] chars = "hello man".toCharArray();
fw.write(chars);
//fw.write(chars,2,2);
fw.close();
}
}
4.3.2、写出字符串,write(String str) 和wtrie(String str,int off,int len)每次可以写出字符串中的数据,更为方便,代码演示:
public class FWrite {
public static void main(String[] args) throws IOException {
//使用文件名创建流对象
FileWriter fw = new FileWriter("d:\\a.txt");
//定义字符串
String msg = "程序员";
//写出从索引2开始,2个字节
fw.write(msg,2,1);
fw.close();
}
}
四、文件复制
1、使用FileInputStream和FileOutputStream来实现文件的拷贝
拷贝的过程应该是一边读一边写
使用FileInputStream和FileOutputStream字节流拷贝文件的时候,文件类型随意,什么样的文件都能拷贝
【代码演示】
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class myclass {
public static void main(String[] args){
FileInputStream fis=null;
FileOutputStream fos=null;
try {
//创建一个输入流
fis=new FileInputStream("D:\\SecureCRT keygen.exe");
//创建一个输出流
fos=new FileOutputStream("F:\\SecureCRT keygen.exe");
//一边读一边写
byte[] bytes=new byte[1024*1024]; //设置一次最多拷贝1MB
int readcount=0;
while((readcount=fis.read(bytes))!=-1){
fos.write(bytes,0,readcount);
}
//输出流最后要刷新
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//分开try不要一起try,一起try的话,其中一个出错,另一个就关不了了
if(fos !=null){
try{
fos.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(fis !=null){
try{
fis.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
2、使用FileReader和FileWriter对文本文件进行复制
文件字符输入流,只能读取普通的文本文件
读取文本内容时,比较方便快捷
注意:Java文件也是普通文本文件(能用记事本编辑的都是普通文本文件,普通文本文件与后缀名无关,不一定是.txt)
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class myclass {
public static void main(String[] args){
FileReader frs=null;
FileWriter fws=null;
try {
//创建一个输入流
frs=new FileReader("F:\\abc.txt");
//创建一个输出流
fws=new FileWriter("D:\\abc.txt");
//一边读一边写
char[] chars=new char[1024*512]; //设置一次最多读取1M
int readcount=0;
while((readcount=frs.read(chars))!=-1){
fws.write(chars,0,readcount);
}
//输出流最后要刷新
fws.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
//分开try不要一起try,一起try的话,其中一个出错,另一个就关不了了
if(fws !=null){
try{
fws.close();
}catch (IOException e) {
e.printStackTrace();
}
}
if(frs !=null){
try{
frs.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
}
}