第12章 输入和输出
一、流概述
流是指一组有序的数据序列,数据源和输出目标可以是文件,网络,压缩包或其他。
二、输入/输出流
所有输入流都是抽象类InputStream(字节输入流)或抽象类Reader(字符输入流)的子类;所有输出流都是抽象类OutputStream(字节输出流)和抽象类Writer(字符输出流)的子类。
1、输入流
InputStream是所有字节输入流的父类,该类主要方法如下,方法出现错误时候会引发IOException异常。
(1)read():从输入流中读取数据的下一个字节,返回0~255之间的int字节值,如果已到末尾,返回-1.
(2)read(byte[] b):从输入流中读入一定长度的字节,并以整数形式返回字节数。
(3)close():关闭此输入流并释放与该数据流关联的所有系统资源。
Java中的字符是Unicode编码,为双字节,而InputStream类处理的是字节流,一般在Java中用Reader类处理字符文本。
2、输出流
OutputStream是所有字节输出流的父类,主要方法如下:
(1)write(int b):将指定的字节写入此输出流。
(2)write(byte[] b):将b.length个字节从指定的byte数组写入流
(3)close():关闭输出流。
三、File类
1、文件的创建与删除
(1)File(String pathname):参数作为路径名创建一个新File实例。
(2)File(String parent,String child):参数分别作为父路径和子路径,创建一个新File实例。
(3)File(File f,String child):f作为父路径对象,child作为子路径字符串。
1 //项目中创建FileTest类,判断D盘的mywork文件夹是否存在work.txt2 //如果存在则删除,如果不存在则创建
3 importjava.io.File;4 public classFileTest {5
6 public static voidmain(String[] args) {7 File file=new File("D:/myword","test.txt");//创建文件对象
8 if(file.exists()){9 file.delete();10 System.out.println("文件已删除");11 }else{12 try{13 file.createNewFile();14 System.out.println("文件已创建");15 }catch(Exception e){16 e.printStackTrace();17 }18 }19 }20 }
2、获取文件信息
File类获取文件本身信息的方法主要包括:
(1)getName():获取文件的名称。
(2)canRead():判断文件是否可读
(3)canWrite():判断文件是否可写入
(4)exists():判断文件是否存在
(5)length():获取文件以字节为单位的长度
(6)getAbsolutePath():获取文件的绝对路径
(7)getParent():获取文件的父路径
(8)isFile():判断文件是否存在(判断对象是否是一个文件?)
(9)isDirectory():判断对象是否是一个目录
(10)isHidden():判断对象是否是隐藏文件
(11)lastMOdified():获取文件最后修改时间
importjava.io.File;public classFileTest {public static voidmain(String[] args) {
File file=new File("D:/myword","test.txt");//创建文件对象
if(file.exists()){
String name=file.getName(); //获取文件名称
long length=file.length(); //获取文件长度
boolean hidden=file.isHidden(); //判断文件是否隐藏
System.out.println("文件名称为:"+name);
System.out.println("文件长度为:"+length);
System.out.println("文件是隐藏文件吗?"+hidden);
}else{
System.out.println("文件不存在!");
}
}
}
//通过renameTo()重命名文件
importjava.io.File;public classFileTest {public static voidmain(String[] args) {
File file=new File("D:/myword","test.txt");//创建文件对象
if(file.exists()){
System.out.println("文件名称为:"+file.getName());//获取文件名称
System.out.println("文件长度为:"+file.length());//获取文件长度
System.out.println("文件是隐藏文件吗?"+file.isHidden());//判断文件是否隐藏
}else{
System.out.println("文件不存在!");
}
File file1=new File("D:/myword","testhaha.txt");if(file1.exists()){
System.out.println("已存在"+file1.getName()+"文件,无法重命名!!");
}else{
file.renameTo(file1);
System.out.println(file.getName()+"文件名称已修改为"+file1.getName());
}
}
}
四、文件输入、输出流
1、如果需要将数据永久保存,可使用FileInputStream和FileOutputStream类与文件建立连接,将需要的数据永久保存到文件里。
FileInputStream是InputStream抽象类的实现类,FileOutputStream是OutputStream抽象类的实现类。
1 //使用FileOutputStream类向文件work写入信息2 //然后通过FileinputStream类将work文件中的数据读取到控制台中
3 import java.io.*;4 public classFileTest {5 public static voidmain(String[] args){6 File file=new File("F:\\...\\STUDY\\lab\\java","work.txt"); //创建文件对象
7 try{8 FileOutputStream out=new FileOutputStream(file); //创建FileOutputStream对象
9 byte bx[]=" tslvcj ".getBytes(); //创建字节型数组
10 out.write(bx); //将数组中信息写入文件
11 out.close(); //关闭输出流
12 }catch(Exception e){13 e.printStackTrace();14 }15 try{16 FileInputStream in=newFileInputStream(file);17 byte by[]=new byte[1024];18 int len=in.read(by); //将文件中字节流数据读取入数组
19 System.out.println("文件中信息是:"+new String(by,0,len));20 in.close();21 }catch(Exception e){22 e.printStackTrace();23 }24
25 }26
27 }
2、FileInputStream和FileOutputStream是针对字节流文件的类,如果对于非单字节编码的文件,可能会出现乱码,一般对字符流文件采用FileReader和FileWriter类。
1 import java.io.*;2 public classFileRwTest {3
4 public static voidmain(String[] args) {5 File file=new File("F:\\霜\\STUDY\\lab\\java","work.txt");6 try{7 FileWriter fwr=newFileWriter(file);8 fwr.write("ilc!");9 fwr.close();10 }catch(Exception e){11 e.printStackTrace();12 }13 try{14 FileReader fr=newFileReader(file);15 char mystr[]=new char[1024];16 int len=fr.read(mystr);17 System.out.println("文件中信息为:"+new String(mystr,0,len));18 fr.close();19 }catch(Exception e){20 e.printStackTrace();21 }22 System.out.println("文件名称为:"+file.getName());23 System.out.println("文件长度为:"+file.length()+"Bytes");24 System.out.println("文件是否隐藏:"+file.isHidden());25 }26 }
1 import java.io.*;2 public classFileTest {3 //显示某一文件类下所有文件的名称
4 public voidshowFileName(File file){5 if(!file.exists()){6 System.out.println("文件不存在!");7 }else{8 if(file.isFile()){9 System.out.println(file.getName());10 }else{11 File[] fd=file.listFiles();12 for(File fx:fd){13 showFileName(fx);14 }15 }16 }17 }18 public static voidmain(String[] args){19 FileTest ft=newFileTest();20 File file=new File("E:\\lab");21 ft.showFileName(file);22 }23 }
1 //删除指定文件或文件夹
2 import java.io.*;3 public classDelFile {4 public voiddelF(File file){5 if(!file.exists()){6 System.out.println("文件不存在!");7 }else{8 if(file.isFile()){9 System.out.println("文件"+file.getName()+"已删除!");10 file.delete();11 }else{12 File[] fa=file.listFiles();13 for(File fx:fa){14 delF(fx);15 }16
17 }18 }19 }20 public static voidmain(String[] args){21 DelFile delTest=newDelFile();22 File file=new File("E:\\lab");23 delTest.delF(file);24 }25 }
五、带缓存的输入、输出流
1、BufferedInputStream类可以对任意InputStream类进行带缓存区的包装以达到性能优化,有两种构造函数:
(1)BufferedInputStream(InputStream in):创建了32个字节的缓存流;
(2)BufferedInputStream(InputStream in,int size):按指定大小创建缓存流。
2、BufferedOutputStream类输出信息和向OutputStream类输入信息完全一样,只不过BufferedOutputStream通过flush()方法强制将缓存区的数据输出结束。其有两种构造方法:
(1)BufferedOutputStream(OutputStream in):创建一个32个字节的缓存区;
(2)BufferedOutputStream(OutputStream in,int size):按指定大小创建缓存流。
3、BufferedReader类与BufferedWriter类分别继承Reader类和Writer类
*BufferedReader类常用方法如下:
(1)read():读取单个字符;
(2)readLine():读取一个文本行,并将其返回为字符串;若无数据,返回null;
(3)writ(String s,int off,int len):写入字符串的某一部分;
(4)flush():刷新该流的缓存。
补充:
一、RandomAccessFile
是java提供的对内容的访问类,可读写操作,顾名思义也可以随机访问文件(即访问文件的任意位置)。
1、Java文件模型:对于Java而言,在硬盘上的文件是byte-byte-byte存储的,是字节数据的集合
2、访问有两种模式:"rw"(读写)、"r"(只读)
RandomAccessFile raf=new RandomAccessFile(file,"rw");
因为随机访问,所以RandomAccessFile类内部含有一个文件指针,第一次打开文件时候指针指向文件开头即pointer=0;随着操作指针会后移。
3、写方法
raf.write(int):只写一个字节(int的后8位),同时指针指向下一个字节位置
4、读取文件
int b=raf.read():读取指针所指的一个字节
5、文件操作完成后一定要关闭
raf.close();
1 importjava.io.File;2 importjava.io.RandomAccessFile;3 importjava.util.Arrays;4
5
6 public classRafDemo {7 public static void main(String[] args) throwsException{8 //创建文件所在目录对应的文件对象
9 File demo=new File("demo");10 if(!demo.exists()){11 demo.mkdir();12 }13 //创建文件对象
14 File file=new File(demo,"raf.dat");15 if(!file.exists()){16 file.createNewFile();17 }18 //创建针对上面文件的文件访问类
19 RandomAccessFile raf=new RandomAccessFile(file,"rw");20 //看一下指针的位置
21 System.out.println(raf.getFilePointer());22
23 raf.write('A');//只写一个字节,A的后8位(低8位)
24 System.out.println(raf.getFilePointer());25 raf.write('B');26
27 //定义一个整数,写入文件28 //但每次只能写一个字节29 //只能通过移位,分别写4词
30 int i=0x7fffffff;31 raf.write(i>>>24);//先写高8位,因为write写的是低8位,所以先右移24位,
32 raf.write(i>>>16);33 raf.write(i>>>8);34 raf.write(i);35 //前面写了6个字节,此时指针指向6
36 System.out.println(raf.getFilePointer());37
38 //可以直接写int型数据
39 raf.writeInt(i);40
41 System.out.println(raf.getFilePointer());42
43 String s="中";44 byte[] gbk=s.getBytes("gbk");45 raf.write(gbk);46 System.out.println(raf.length());47
48 //读文件必须先把指针移动到头部
49 raf.seek(0);50 //一次性读取到字符数组
51 byte[] buf=new byte[(int)raf.length()];52 raf.read(buf);53
54 System.out.println(Arrays.toString(buf));55 for (byteb : buf) {56 System.out.print(Integer.toHexString(b&0xff)+" ");57 }58 //字符数组构造成字符串
59 String s1=newString(buf);60 System.out.println(s1);61 raf.close();62 }63
64 }
二、IO流
分为字节流和字符流
1、字节流
(1)InputStream\OutputStream
InputStream抽象了应用程序读取数据的方式;OutputStream抽象了应用程序写出数据的方式;
(2)达到文件结尾,即EOF=End 读到-1就是读到结尾
(3)输入流基本方法(一般即读出的操作)
int b=in.read();读取一个字节无符号填充到整型b的低8位.读到-1即结尾
in.read(byte[] buf):读取数据填充到字节数组buf中
in.read(byte[] buf,int start,int size):从buf的start位置开始读取size长度的数据到字节数组buf中
(4)输出流基本方法(一般即写入的操作)
out.write(int b):只写一个byte(b的低八位)到out流
out.write(byte[] buf):将buf字节数组写入流
out.write(byte[] buf,int start,int size)
(5)InputStream\OutputStream的子类:FileInputStream和FileOutputStream
FileInputStream具体实现了在文件上读取数据
FileOutputStream具体实现了在文件上写数据
1 importjava.io.File;2 importjava.io.IOException;3 import java.io.*;4
5
6 public classIOUtil {7 /*
8 * 读取指定文件内容,按照16进制输出到控制台9 * 并且每输出10个byte换行10 */
11
12 public static void printHex(String fileName) throwsException{13 //把文件作为字节流进行读操作
14 FileInputStream in=newFileInputStream(fileName);15 intb;16 int i=1;17 while((b=in.read())!=-1){18 if(b<=0xf){19 System.out.print("0");20 }21 System.out.print(Integer.toHexString(b)+" ");22 if(i++%10==0){23 System.out.println();24 }25 }26 System.out.println();27 System.out.println("文件一共有"+i+"个字节!");28 in.close();29 }30
31 }32
33 public classIOUtilTest1 {34
35 public static voidmain(String[] args) {36 try{37 IOUtil.printHex("F:\\IOUtil.java");38 } catch(Exception e) {39 e.printStackTrace();40 }41 }42
43 }
上面程序中用in.read()通过单字节读取文件,对于较大的文件,一般用in.read(byte[] buf,int start,int size)进行批量读取以节约时间。
针对上面的程序,优化如下:
1 public static void printHexByByteArray(String fileName)throwsException{2 FileInputStream in=newFileInputStream(fileName);3 byte[] buf=new byte[20*1024];4 //从in中批量读取字节,放入到buf字节数组中,从第0个位置开始放置,最多放buf.length个字节;返回为读取的个数
5 int bytes=in.read(buf,0,buf.length);6 int j=1;7 for (int i=0;i
18 }
利用FileOutputStream写文件,并完成文件的复制功能。
1 importjava.io.FileInputStream;2 importjava.io.FileOutputStream;3 importjava.io.IOException;4 importjava.io.File;5 public classFileOutDemo1 {6 public static void copyFile(File srcFile,File destFile)throwsIOException{7 if(!srcFile.exists()){8 throw new IllegalArgumentException("文件不存在!");9 }10 if(!srcFile.isFile()){11 throw new IllegalAccessError(srcFile+"不是文件");12 }13 FileInputStream in=newFileInputStream(srcFile);14 FileOutputStream out=newFileOutputStream(destFile);15 byte[] buf=new byte[8*1024];16 intb;17 while((b=in.read(buf,0,buf.length))!=-1){18 out.write(buf,0,b);19 out.flush();//刷新缓冲区
20 }21 in.close();22 out.close();23
24 }25 public static void main(String[] args)throwsIOException{26 File demo=new File("demo");27 if(!demo.exists()){28 demo.mkdir();29 }30 //如果该文件不存在,则直接创建,如果存在删除后创建
31 FileOutputStream out=new FileOutputStream("demo/out.txt");32 out.write('a');33 out.write('b');34 byte[] buf="中国".getBytes("gbk");35 out.write(buf);36 File srcFile=new File("demo/out.txt");37 File outCopy=new File("demo/outCopy.txt");38 copyFile(srcFile,outCopy);39
40
41 }42
43 }
三、DataOutputStream/DataInputStream是对“流”功能的扩展,可以更方便的读取int,long,字符等类型数据
1 import java.io.*;2 public classDosDemo {3 public static void main(String[] args)throwsException{4 String file="demo/dos.txt";5 File demo=new File("demo");6 if(!demo.exists()){7 demo.mkdir();8 }9 //DataOutputStream类的构造函数的参数是FileOutputStream类对象
10 DataOutputStream dos=newDataOutputStream(11 newFileOutputStream(file));12 dos.writeInt(10);13 dos.writeInt(-10);14 dos.writeLong(10);15 dos.writeDouble(10.5);16 dos.writeUTF("中国");17 dos.writeChars("中国");18 dos.close();19 DataInputStream dis=newDataInputStream(20 newFileInputStream(file));21 int i=dis.readInt();22 System.out.println(i);23 i=dis.readInt();24 System.out.println(i);25 long l=dis.readLong();26 System.out.println(l);27 double d=dis.readDouble();28 System.out.println(d);29 String s=dis.readUTF();30 System.out.println(s);31 dis.close();32 }33 }
4、BufferedInputStream/BufferedOutputStream:为IO提供了带缓冲区的操作,一般打开文件进行写入或读取操作时,都会加上缓冲,这种流模式提高了IO的性能。
比喻:从应用程序中把数据输出到文件,相当于把一缸水倒入外面的一个水箱
那么:
FileOutputStream类的write()方法相当于一滴一滴倒入;
DataOutputStream类的writeXxx()方法(包括writeInt,writeLong,writeUTF等)相当于一瓢一瓢地倒入;
而BufferOutputStream类的write()方法相当于先一瓢一瓢倒入桶中,再从桶中倒入水箱。
下面是通过不同IO流类的复制文件方法所耗时的对比:
/*利用带缓冲的字节流类
* 进行文件的复制*/
import java.io.*;public classCopyFile {//Buffered流操作
public static void copyFileByBuffer(File srcFile,File destFile)throwsException{if(!srcFile.exists()){throw new IllegalArgumentException("文件不存在!");
}if(!srcFile.isFile()){throw new IllegalAccessError(srcFile+"不是文件");
}
BufferedInputStream bis=newBufferedInputStream(newFileInputStream(srcFile));
BufferedOutputStream bos=newBufferedOutputStream(newFileOutputStream(destFile));intc;while((c=bis.read())!=-1){
bos.write(c);
bos.flush();
}
bis.close();
bos.close();
}//单字节流操作
public static void copyFileByByte(File srcFile,File destFile)throwsException{if(!srcFile.exists()){throw new IllegalArgumentException("文件不存在!");
}if(!srcFile.isFile()){throw new IllegalAccessError(srcFile+"不是文件");
}
FileInputStream bis=newFileInputStream(srcFile);
FileOutputStream bos=newFileOutputStream(destFile);intc;while((c=bis.read())!=-1){
bos.write(c);
}
bis.close();
bos.close();
}//字节批量操作
public static void copyFileByArray(File srcFile,File destFile)throwsException{if(!srcFile.exists()){throw new IllegalArgumentException("文件不存在!");
}if(!srcFile.isFile()){throw new IllegalAccessError(srcFile+"不是文件");
}
FileInputStream bis=newFileInputStream(srcFile);
FileOutputStream bos=newFileOutputStream(destFile);intc;byte[] ba=new byte[2024*1024];while((c=bis.read(ba,0,ba.length))!=-1){
bos.write(ba,0,ba.length);
}
bis.close();
bos.close();
}//字节流操作
public static void copyFileByData(File srcFile,File destFile)throwsException{if(!srcFile.exists()){throw new IllegalArgumentException("文件不存在!");
}if(!srcFile.isFile()){throw new IllegalAccessError(srcFile+"不是文件");
}
DataInputStream bis=newDataInputStream(newFileInputStream(srcFile));
DataOutputStream bos=newDataOutputStream(newFileOutputStream(destFile));intc;while(bis.available()>0&&(c=bis.read())!=-1){
bos.write(c);
}
bis.close();
bos.close();
}
}importjava.io.File;public classcopyTest {public static void main(String[] args)throwsException {
File demo=new File("demo");if(!demo.exists()){
demo.mkdir();
}
File srcFile=new File("demo/but.wma");
File destFile=new File("demo/c1.wma");
File destFile1=new File("demo/c2.wma");
File destFile2=new File("demo/c3.wma");
File destFile3=new File("demo/c4.wma");long start=System.currentTimeMillis();
CopyFile.copyFileByBuffer(srcFile, destFile);long end=System.currentTimeMillis();
System.out.println("BufferedCopy用了"+(double)(end-start)/1000+"秒!");
start=System.currentTimeMillis();
CopyFile.copyFileByData(srcFile, destFile1);
end=System.currentTimeMillis();
System.out.println("DataCopy用了"+(double)(end-start)/1000+"秒!");
start=System.currentTimeMillis();
CopyFile.copyFileByByte(srcFile, destFile2);
end=System.currentTimeMillis();
System.out.println("ByteCopy用了"+(double)(end-start)/1000+"秒!");
start=System.currentTimeMillis();
CopyFile.copyFileByArray(srcFile, destFile3);
end=System.currentTimeMillis();
System.out.println("ArrayCopy用了"+(double)(end-start)/1000+"秒!");
}
}/*可以看出批量操作的复制时间最短
* 其次是带缓冲流
* Data流包装类和单字节读取时间最慢
* 但也可以看出批量操作的时间比带缓冲流节约时间效果非常明显*/
5、字符流:一次处理一个字符,字符的底层仍然是基本的字节序列。一般操作的都是文本和文本文件。
1、InputStreamRead:完成byte流解析为char流,按照编码解析。
OutputStreamWriter:提供char流到byte流,按照编码解析。
1 import java.io.*;2
3
4 public classIsrAndOswDemo {5 public static void main(String[] args)throwsIOException {6 //先创建FileInputStream对象in,用做创建InputStreamReader类对象的构造函数的参数
7 FileInputStream in=new FileInputStream("H:\\javalab\\1.txt");8 InputStreamReader isr=new InputStreamReader(in);//参数不加编码类型则采用项目默认的编码
9
10 FileOutputStream out=new FileOutputStream("H:\\javalab\\2.txt");11 OutputStreamWriter osw=newOutputStreamWriter(out);12 //单字符读取写入
13 intc;14 while((c=isr.read())!=-1){15 System.out.print((char)c);16 osw.write((char)c);17 osw.flush();18 }19 //批量读取,放入buff字符数组,从0开始放,最多放buff.length个字符,返回的是读到的字符个数
20 /*char[] buff=new char[8*1024];21 int c;22 while((c=isr.read(buff,0,buff.length))!=-1){23 String s=new String(buff,0,c);24 System.out.println(s);25 osw.write(buff,0,c);26 osw.flush();27 }*/
28 in.close();29 isr.close();30 }31 }
2、FileReader/FileWriter:比InputStreamReader/OutputStreamWriter操作要方便;
可以直接对文件进行字符流构造
1 import java.io.*;2 public classFrAndFwDemo {3
4 public static void main(String[] args)throwsIOException {5 FileReader fr=new FileReader("H:\\javalab\\3.txt");6 FileWriter fw=new FileWriter("H:\\javalab\\4.txt",true);7 char[] buff=new char[2*1024];8 intc;9 while((c=fr.read(buff,0,buff.length))!=-1){10 String s=new String(buff,0,c);11 System.out.println(s);12 fw.write(buff,0,c);13 fw.flush();14 }15 fr.close();16 fw.close();17 }18 }
3、字符流的过滤器:BufferedReader最强大的功能是readLine方法,一次读一行;
BufferedWriter/PrintWriter:写操作。
1 import java.io.*;2 public classBrAndBwAndPwDemo {3 public static void main(String[] args)throwsIOException{4 //对文件进行读写操作5 //Buffered过滤文件流构造比较方法
6 BufferedReader br=newBufferedReader(7 newInputStreamReader(8 new FileInputStream("H:\\javalab\\4.txt")));9 /*BufferedWriter bw=new BufferedWriter(10 new OutputStreamWriter(11 new FileOutputStream("H:\\javalab\\5.txt")));12 String line;13 while((line=br.readLine())!=null){14 System.out.println(line);//一次读一行并不能识别换行15 bw.write(line);//没法单独写一行16 bw.newLine();//单独写换行操作17 bw.flush();18 }19 */
20 //PrintWriter的构造比较简单
21 PrintWriter pw=new PrintWriter("H:\\javalab\\5.txt");22 //PrintWriter pw1=new PrintWriter("H:\\javalab\\5.txt",true);//自动带清空缓冲区功能
23 String line;24 while((line=br.readLine())!=null){25 //pw.print(line);
26 pw.println(line);//换行用println()
27 pw.flush();28 }29 br.close();30 pw.close();31
32
33
34 }35
36 }