如果要进行文件内容的操作,必须依靠数据流完成,而数据流分为两种:
字节流:InpuStream(字节输入流)、OutputStream(字节输出流)
字符流:Reader(字符输入流)、Writer(字符输出流)
字符比字节处理的多,但使用哪个,基本流程都一样
范例:
创建File类对象,主要是指明要操作的文件路径
通过字节流或字符流的子类为父类实例化
进行文件的读写操作
关闭数据流(close())
字节输出流:OutputStream
字节输出流主要以byte数据为主
输出单个字节:public abstract void write(int b) throws IOException
输出全部字节数组:public void write(byte[] b) throws IOException
输出部分字节数组:public void write(byte[] b, int off, int len) throws IOException //重点
OutputStream是抽象类,文件的输出操作需要子类FileOutputStream,此类有两个常用构造
构造方法:public FileOutputStream(File file) throws FileNotFoundException 新内容覆盖文件
构造方法:public FileOutputStream(File file, boolean append) throws FileNotFoundException 追加文件内容
范例:实现文件的输出(往文件里写内容)
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定义要输出文件的File类对象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //输出信息的时候文件可以不存在,目录必须存在 if(!file.getParentFile().exists()) { //父路径不存在 file.getParentFile().mkdirs(); //创建父路径 } //第二部:利用OutputStream的子类为父类实例化 OutputStream output = new FileOutputStream(file); //第三步:输出文字信息 String msg = "******"; //字符串 //为了方便输出,需要将字符串变为字节数组 byte data[] = msg.getBytes(); // 变为了字节数组 output.write(data); //输出数据 , 创建了test.TXT,并写入了*****数据 output.close(); } }
实现只输出部分内容:
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定义要输出文件的File类对象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //输出信息的时候文件可以不存在,目录必须存在 if(!file.getParentFile().exists()) { //父路径不存在 file.getParentFile().mkdirs(); //创建父路径 } //第二部:利用OutputStream的子类为父类实例化 OutputStream output = new FileOutputStream(file); //第三步:输出文字信息 String msg = "adfasdfadsfasdfasdfsadfasdf"; //字符串 //为了方便输出,需要将字符串变为字节数组 byte data[] = msg.getBytes(); // 变为了字节数组 output.write(data, 0, 10); //输出数据 , 从0开始输出10个 output.close(); }
使用循环的方式单个字节的输出
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定义要输出文件的File类对象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //输出信息的时候文件可以不存在,目录必须存在 if(!file.getParentFile().exists()) { //父路径不存在 file.getParentFile().mkdirs(); //创建父路径 } //第二部:利用OutputStream的子类为父类实例化 OutputStream output = new FileOutputStream(file); //第三步:输出文字信息 String msg = "adfasdfadsfasdfasdfsadfasdf"; //字符串 //为了方便输出,需要将字符串变为字节数组 byte data[] = msg.getBytes(); // 变为了字节数组 for(int x = 0; x < data.length; x++) { output.write(data[x]); //输出数据 } output.close(); } }
现在发现每当执行完成后之前的内容都被覆盖了,所以也可以进行数据的追加操作:
范例:追加数据(加入新数据 之前的数据不变 ),第二部加个true
import java.io.File; import java.io.FileOutputStream; import java.io.OutputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定义要输出文件的File类对象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //输出信息的时候文件可以不存在,目录必须存在 if(!file.getParentFile().exists()) { //父路径不存在 file.getParentFile().mkdirs(); //创建父路径 } //第二部:利用OutputStream的子类为父类实例化 OutputStream output = new FileOutputStream(file, true); //此处为true,表示追加操作 //第三步:输出文字信息 String msg = "111adfasdfadsfasdfasdfsadfasdf"; //字符串 //为了方便输出,需要将字符串变为字节数组 byte data[] = msg.getBytes(); // 变为了字节数组 output.write(data); //输出数据 output.close(); } }
进行换行操作使用“\r\n”
字节输入流InputStream
可以实现数据读取
三个数据读取方法:
1. 读取单个字节:public abstract int read() throws IOException //每次执行此方法将读取单个字节的数据,如果已经去取完成了,没数据了,那么最后返回的是-1
2. 读取数据到字节数组中:public int read(byte[]b) throws IOException //每次讲数据读取到数组中,那么会返回一个读取长度的数据,如果没有数据,返回长度为-1,要考虑两种情况:1.要读取的内容大于开辟数组的内容,长度就是整个数组的长度 2.要读取的内容小于开辟的数组内容,长度就是全部最后的内容长度,数组装不满
3. 读取 部分内容到字节数组:public int read(byte[]b,int off, int len) throws IOException //每次读取内容到部分字节数组,只允许读取满限制的数组的字节个数,此方法依然返回读取的长度。
InputStream是抽象类,所以要进行文件的读取使用FileInputStream子类,子类定义的构造方法:public FileInputStream(File file) throws FileNotFoundException
范例:实现从txt的数据读取
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class Hello{ public static void main(String[] args) throws Exception { //第一步:定义要输出文件的File类对象 File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); //第二步:实例化InputStream类对象 InputStream input = new FileInputStream(file); //第三步:实现数据读取操作 byte data[] = new byte[1024]; int len = input.read(data); //将数据读取到数组中 System.out.println("读取的内容:【" + new String(data,0,len) +"】"); //第四步:关闭输入流 input.close(); } }
read()方法可以实现单个字节数据读取操作,用此方法实现单个字节数据的读取
范例 :读取单个字节”
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); InputStream input = new FileInputStream(file); byte data[] = new byte[1024]; int foot = 0; //控制保存的角标索引 int temp = 0; //接收每次保存的数据 do { temp = (byte)input.read(); //读取出来的数据保存到字节数组中 if(temp != -1) { //现在有数据 data[foot ++] = (byte) temp; } } while(temp != -1); //表示后面可能还有数据 System.out.println("读取的内容:【" + new String(data,0,foot) +"】"); input.close(); } }
以上使用了do...while,实际开发中都用while:
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); InputStream input = new FileInputStream(file); byte data[] = new byte[1024]; int foot = 0; //控制保存的角标索引 int temp = 0; //接收每次保存的数据 while((temp = input.read())!= -1 ) { data[foot ++] = (byte)temp; } System.out.println("读取的内容:【" + new String(data,0,foot) +"】"); input.close(); } }
字符输出流:Writer
Writer是进行字符输出操作的抽象类
之所以提供一个Writer类,是因为这个类的输出方法有一个特别好用的:
输出字符串:public void write(String str) throws IOException //重点
范例:使用Writer输出数据
import java.io.File; import java.io.FileWriter; import java.io.Writer; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); if(file.getParentFile().exists()) { file.getParentFile().mkdirs(); } Writer out = new FileWriter(file); String data = "测试数据测试数据测试数据测试数据测试数据" ; out.write(data); //直接输出字符串 out.close(); } }
若要追加增加数据,之前的不变:
Writer out = new FileWriter(file, true);
虽然Wirter类提供字符数组的输出操作能力,但本质上讲使用Writer类就意味着要执行字符串的直接输出。
字符输入流:Reader
数据读取:public int read(char[] cbuf) throws IOException
范例:读取数据
import java.io.File; import java.io.FileReader; import java.io.Reader; public class Hello{ public static void main(String[] args) throws Exception { File file = new File("F:" + File.separator + "hello" + File.separator + "Test.txt"); if(file.exists()) { Reader in = new FileReader(file); char data[] = new char[1024]; int len = in.read(data); //向字符数组中保存数据,返回长度 System.out.println(new String(data, 0, len)); in.close(); } } }
Reader与InputStream类相比除了数据类型的差别之外,操作上没有优势
字节流与字符流的区别
这两种流的区别就好比数据库中的BLOB与CLOB区别
CLOB保存大文本数据,都是字符数据
BLOB保存二进制数据,例如:电影、图片、文字,都是字节数据
通过任何终端(网络、文件)读取或者输出的数据都一定是字节,但字符是通过内存处理后的数据。
字符输入:字符输入的时候字节(磁盘)自动转换为字符(内存)
字符输出:字符(内存)自动转换为字节(磁盘)
在利用字符流输出的时候,所有的内容实际上都只是输出到了缓冲区中(内存)。在使用close()关闭的时候会将缓冲区的数据输出,如果不关闭就不发进行输出,此时可以利用flush()进行强制刷新
字符使用到了缓冲区,而字节没有使用到缓冲区。如果处理中文使用字符流,其他任何数据都是用字节流。
综合案例:文件拷贝
是模拟dos系统中的copy命令完成
编写一个文件拷贝程序,可实现任意文件拷贝操作,通过初始化参数输入拷贝的源文件以及拷贝的目标文件路径,本程序暂不考虑类的设计。
dos拷贝命令:“copy 路径1 路径2”
两种实现思路:
思路一:开辟一个数组,将需拷贝的内容读取到数组之中,而后一次性输出到目标路径中
思路二:采用边读边写的方式进行拷贝,不是一次性读取
第一种方式如果文件小没问题,5M左右。如果文件大,基本内存就被沾满了,
范例:初期实现
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class CopyDemo { public static void main(String[] args) throws Exception { if(args.length != 2) { //参数内容必须是两个,一个源文件路径一个目标文件路径 System.out.println("错误的命令,格式为:CopyDemo 源文件路径 目标文件路径。"); System.exit(1); //退出程序 } //接下来验证源文件是否存在 File inFile = new File(args[0]); if(!inFile.exists()) { System.out.println("路径错误,请确定源文件路径正确。"); System.exit(1); } //如果拷贝的目标文件存在,则也不应该进行拷贝 File outFile = new File(args[1]); if(outFile.exists()) { //目标文件已经存在 System.out.println("拷贝的路径文件已经存在,请更换路径。"); System.exit(1); } long start = System.currentTimeMillis(); InputStream in = new FileInputStream(inFile); OutputStream out = new FileOutputStream(outFile); copy(in,out); //开始文件拷贝 in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("花费的时间:" + (end - start) ); } public static void copy(InputStream input, OutputStream output) throws Exception { int temp = 0; //保存每次读取的字节量 while((temp = input.read()) != -1 ) { //每次读取一个字节 output.write(temp); } } }
要先执行以下再输入路径。此种方法只能复制很小的文件。
用数组来提升拷贝性能,可以将数据读取到数组中,而后一次性将数组输出。
修改拷贝方法:
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; public class CopyDemo { public static void main(String[] args) throws Exception { if(args.length != 2) { //参数内容必须是两个,一个源文件路径一个目标文件路径 System.out.println("错误的命令,格式为:CopyDemo 源文件路径 目标文件路径。"); System.exit(1); //退出程序 } //接下来验证源文件是否存在 File inFile = new File(args[0]); if(!inFile.exists()) { System.out.println("路径错误,请确定源文件路径正确。"); System.exit(1); } //如果拷贝的目标文件存在,则也不应该进行拷贝 File outFile = new File(args[1]); if(outFile.exists()) { //目标文件已经存在 System.out.println("拷贝的路径文件已经存在,请更换路径。"); System.exit(1); } long start = System.currentTimeMillis(); InputStream in = new FileInputStream(inFile); OutputStream out = new FileOutputStream(outFile); copy(in,out); //开始文件拷贝 in.close(); out.close(); long end = System.currentTimeMillis(); System.out.println("花费的时间:" + (end - start) ); } public static void copy(InputStream input, OutputStream output) throws Exception { int temp = 0; //保存每次读取的字节量 byte data[] = new byte[2048]; //数据向数组中读取 while((temp = input.read(data)) != -1 ) { //每次读取一个字节 output.write(data, 0 , temp); //输出数组 } } }
特别快。
对于File、InputStream、OutputStream最直接操作就体现在本程序中