利用字节流和字符流实现文件内容操作
如果要进行文件内容的操作,那么必须依靠数据流完成,而数据流主要分为两种
(1)字节流:InputStream(字节输入流)、OutputStream(字节输出流)
(2)字符流:Reader(字符输入流)、Writer(字符输出流)
字符要比字节处理的更多一些。但是不管使用的是字节流还是字符流,其基本的操作流程都是一样的,以文件操作为例:
1.创建File类对象,主要是指明要操作的文件路径;
2.通过字节流或字符流的子类为父类实例化;
3.进行文件的读、写操作;
4.关闭数据流(close())。
1.1 字节输出流:OutputStream
字节输出流主要是以操作byte数据为主的,首先来观察java.io.OutputStream的定义结构:public abstract class OutputStream implements Closeable, Flushable {}
OutputStream继承了Closeable和Flushable接口
除了close()和flush()两个方法之外,还定义有三个重要的输出操作方法。
(1)输出单个字节:public abstract void write(int b) throws IOException;
(2)输出全部字节数组:public void write(byte b[]) throws IOException;
(3)输出部分数组:public void write(byte b[], int off, int len) throws IOException;
但是OutputStream是一个抽象类,按照抽象类的基本原则来讲,要想去等OutputStream类的实例化对象,那么一定要依靠子类,如果要进行文件的输出操作则可以使用FileOutputStream,在这个类中提供了两个常用构造
构造方法:public FileOutputStream(File file) throws FileNotFoundException
构造方法: public FileOutputStream(File file, boolean append) throws FileNotFoundException
范例:实现文件的输出
package day2;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
//输出信息的时候文件可以不存在,但是目录必须存在
if(!file.getParentFile().exists()){//父路径不存在
file.getParentFile().mkdirs();//创建父路径
}
//第二部:利用OutputStream的子类为父类进行实例化
OutputStream outputStream = new FileOutputStream(file);
//第三部:输出文字信息
String msg = "小猫从小有个梦,想去嵩山少林学武功!!!";
//为了方便输出,需要把字符串变为字节数组
byte data[] = msg.getBytes();
//输出数据
outputStream.write(data);
outputStream.close();
}
}
范例:输出部分数据
package day2;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
//输出信息的时候文件可以不存在,但是目录必须存在
if(!file.getParentFile().exists()){//父路径不存在
file.getParentFile().mkdirs();//创建父路径
}
//第二部:利用OutputStream的子类为父类进行实例化
OutputStream outputStream = new FileOutputStream(file);
//第三部:输出文字信息
String msg = "小猫从小有个梦,想去嵩山少林学武功!!!";
//为了方便输出,需要把字符串变为字节数组
byte data[] = msg.getBytes();
//输出数据
outputStream.write(data,2,14);
outputStream.close();
}
}
循环输出:
for(int i=0;i<data.length;i++){
outputStream.write(data[i]);
}
但是发现每当执行完成之后所有的内容都被覆盖了,所以也可以进行数据的追加操作。
范例:追加数据
//第二部:利用OutputStream的子类为父类进行实例化
OutputStream outputStream = new FileOutputStream(file,true);
如果换行使用:\r\n
//第三部:输出文字信息
String msg = “小猫从小有个梦,想去嵩山少林学武功!!!\r\n”;
1.2 字节输入流:InputStream
InputStream可以实现数据读取操作,在java中的InputStream类中的定义如下:
在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
范例:实现数据读取
package day2;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
//第二部:实例化InputStream对象
InputStream inputStream = new FileInputStream(file);
//实现数据的读取操作
byte data[] = new byte[1024];
int len = inputStream.read(data);//将数据读取到数组之中
System.out.println("读取的内容:【"+new String(data,0,len) + "】");
inputStream.close();
}
}
在InputStream类中提供了read()方法,这个方法可以实现单个字节数据读取操作,下面运用循环的方法采用此方法 实现单个字节数据读取
范例:读取单个字节
package day2;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
//第二部:实例化InputStream对象
InputStream inputStream = new FileInputStream(file);
//实现数据的读取操作
byte data[] = new byte[1024];
int foot = 0;
int temp = 0;
do{
temp = inputStream.read();
if(temp != -1){
data[foot ++] = (byte) temp;
}
}while (temp != -1);
System.out.println("读取的内容:【"+new String(data,0,foot) + "】");
inputStream.close();
}
}
以上使用的是do…while循环操作,但是太麻烦,在实际中一般会采用while实现循环操作
package day2;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
//第二部:实例化InputStream对象
InputStream inputStream = new FileInputStream(file);
//实现数据的读取操作
byte data[] = new byte[1024];
int foot = 0;
int temp = 0;
while ((temp = inputStream.read()) != -1){
data[foot++] = (byte) inputStream.read();
}
System.out.println("读取的内容:【"+new String(data,0,foot) + "】");
inputStream.close();
}
}
1.3 字符输出流Writer
定义: JDK1.1
public abstract class Writer implements Appendable, Closeable, Flushable {
}
Writer是进行字符输出操作的使用的类,这个类是属于抽象类
范例:
在这里插入代码片
package day2;
import java.io.*;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
if(!file.getParentFile().exists()){
file.getParentFile().mkdirs();
}
Writer out = new FileWriter(file);
String date = "哈哈哈,好好生活,天天向上";
out.write(date);
out.close();
}
}
虽然Writer类提供有字符数组的输出能力,但是从本质上来讲使用Writer类就意味着要执行字符串的直接输出。字符流是最适合操作中文的,但是并不是意味着字节流就无法操作中文。
1.4字符输入流Reader
Reader也是一个抽象类,
范例:
package day2;
import java.io.*;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
if(file.getParentFile().exists()){
Reader reader = new FileReader(file);
char data[] = new char[1024];
//向字符数组保存数据,返回长度
int len = reader.read(data);
System.out.println(new String(data,0,len));
reader.close();
}
}
Reader与InputStream类相比出来数据类型的差别之外,操作上没有优势。
1.5 字节流与字符流的区别
这两种流的区别就好比数据库中的BLOB,CLOB
CLOB:保存大文本数据;——>字符数据
BLOB:保存二进制数据,例如:电影、图片、文字 ——>字节数据
通过任何终端(网络、文件)读取或者输出的数据都一定是字节,但字符是经常处理后的数据。
字符输入:字节(磁盘)自动转换为字符(内存)
字符输出:字符(内存)自动转换为字节(磁盘)
在利用字符流输出的时候,所有的内容实际上都只是输出到缓冲区中(内存)。在使用close()方法关闭的时候会将缓冲区的数据输出,如果没有关别,那么就将无法进行输出,此时可以利用flush()进行强制刷新。
字符使用到了缓冲区,而字节没有使用到缓冲区。
注:如果处理中文使用字符流,其他的任何数据都使用字节流,字节流和字符流是可以相互转换的。
1.6 转换流
在现在给出的字节流与字符流的操作之中,可以发现是各有各的特点,为此在java中提供了两个转换的流:InputStreamReader、OutputStreamWriter。
InputStreamReader
public class InputStreamReader extends Reader {}
public InputStreamReader(InputStream in
OutputStreamWriter
public class OutputStreamWriter extends Writer {}
public OutputStreamWriter(OutputStream out)
范例
package day2;
import java.io.*;
public class FileDemo {
public static void main(String[] args) throws Exception{
//第一步:定义要输出的File对象
File file = new File("F:"+File.separator +"java8"+File.separator +"file" +File.separator+"test"+File.separator+"my.txt" );
if(file.getParentFile().exists()){
OutputStream outputStream = new FileOutputStream(file);
//因为writer存在有直接输出字符串的方法
Writer out = new OutputStreamWriter(outputStream);
out.write("情深深雨蒙蒙!!!");
out.close();
outputStream.close();
}
}
}
1.7 文件复制(综合运用)
文件复制是模拟dos系统中的copy命令完成。
编写一个文件拷贝程序,可以实现任意的文件拷贝操作,通过初始化输入参数拷贝的源文件路径以及拷贝的目标文件路径,本程序可以暂拷贝命令:copy e:\my.jpg e:\hello.jpg
如果要想实现这种拷贝操作,可以有以下两种操作思路:
(1)开辟一个数组,将所需拷贝的内容读取到数组之中,而后一次性输出到目标文件路径中;
(2)采用边读边写的方式进行拷贝,不是一次性读取
注:第一种方法问题在于,如果文件小没有问题,5M左右,如果文件大,基本上内存就被占满了
范例:初期实现
package day2;
import java.io.*;
public class CopyDemo {
public static void main(String[] args) throws Exception {
//如果只选拷贝命令,必须通过初始化参数传递源文件路径及目标文件路径
if(args.length != 2){//参数文件必须是两个
System.out.println("错误的命令,格式应该为 copy 源文件路径 目标文件路径");
//退出程序操作
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 startTime = System.currentTimeMillis();
InputStream input = new FileInputStream(inFile);
OutputStream output = new FileOutputStream(outFile);
copy(input,output);
input.close();
output.close();
long endTime = System.currentTimeMillis();
System.out.println("拷贝文件花费时间:" + (endTime - startTime));
}
}
public static void copy(InputStream input,OutputStream output) throws Exception{
//保存每次读取的字节量
int temp = 0;
while ((temp = input.read()) != -1){//每次读取一个字节
output.write(temp);
}
}
}
以上代码执行太慢了,可以利用数组来提升性能。将数据读取到数组中,而后一次性将数组输出。
范例:
package day2;
import java.io.*;
public class CopyDemo {
public static void main(String[] args) throws Exception {
//如果只选拷贝命令,必须通过初始化参数传递源文件路径及目标文件路径
if(args.length != 2){//参数文件必须是两个
System.out.println("错误的命令,格式应该为 copy 源文件路径 目标文件路径");
//退出程序操作
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 startTime = System.currentTimeMillis();
InputStream input = new FileInputStream(inFile);
OutputStream output = new FileOutputStream(outFile);
copy(input,output);
input.close();
output.close();
long endTime = System.currentTimeMillis();
System.out.println("拷贝文件花费时间:" + (endTime - startTime));
}
}
public static void copy(InputStream input,OutputStream output) throws Exception{
//保存每次读取的数量
int temp = 0;
byte data[] = new byte[1024];
//数据向数组中读取
while ((temp = input.read(data)) != -1){
output.write(data,0,temp);
}
}
}