Download presentation
Presentation is loading. Please wait.
1
JAVA语言程序设计 第六章 输入/输出流和文件 郑莉
2
目录 6.1 输入/输出流 6.2 文件读写 6.3 本章小结
3
6.1.1 I/O流的概念 I/O流(Input/Output) 输入/输出流 在Java中将信息的输入与输出过程抽象为I/O流
输入是指数据流入程序 输出是指数据从程序流出 一个流就是一个从源流向目的地的数据序列 IO流类一旦被创建就会自动打开 通过调用close方法,可以显式关闭任何一个流,如果流对象不再被引用,Java的垃圾回收机制也会隐式地关闭它
4
6.1.1 I/O流的概念(续) 输入/输出流 输入流 输出流 为了从信息源获取信息,程序打开一个输入流,程序可从输入流读取信息
当程序需要向目标位置写信息时,便需要打开一个输出流,程序通过输出流向这个目标位置写信息
5
6.1.1 I/O流的概念(续) ——源和目标的类型 输入/输出流 对象 源? 目标? 或两者? disk file
running program monitor keyboard Internet connection image scanner mouse Both Destination Source
6
6.1.1 I/O流的概念(续) ——读写数据的方法 输入/输出流
不论数据从哪来,到哪去,也不论数据本身是何类型,读写数据的方法大体上都是一样的: 读 写 打开一个流 读信息 关闭流 写信息
7
6.1.2 预定义的I/O流类概述 输入/输出流 输入/输出流可以从以下几个方面进行分类 从流的方向划分 从流的分工划分 从流的内容划分
输入流 输出流 从流的分工划分 节点流 处理流 从流的内容划分 面向字符的流 面向字节的流
8
6.1.2 预定义的I/O流类概述(续) ——java.io包的顶级层次结构
输入/输出流 面向字符的流:专门用于字符数据 面向字节的流:用于一般目的
9
6.1.2 预定义的I/O流类概述(续) ——面向字符的流
输入/输出流 面向字符的流 针对字符数据的特点进行过优化,提供一些面向字符的有用特性 源或目标通常是文本文件
10
6.1.2 预定义的I/O流类概述(续) ——面向字符的流
输入/输出流 实现内部格式和文本文件中的外部格式之间转换 内部格式:16-bit char 数据类型 外部格式: UTF(Universal character set Transformation Format):很多人称之为"Universal Text Format" 包括ASCII 码及非ASCII 码字符,比如: 斯拉夫(Cyrillic)字符, 希腊字符,亚洲字符等
11
6.1.2 预定义的I/O流类概述(续) ——面向字符的流
输入/输出流 面向字符的抽象类——Reader和Writer java.io包中所有字符流的抽象基类 Reader提供了输入字符的API Writer提供了输出字符的API 它们的子类又可分为两大类 节点流:从数据源读入数据或往目的地写出数据 处理流:对数据执行某种处理 多数程序使用这两个抽象类的一系列子类来读入/写出文本信息 例如FileReader/FileWriter用来读/写文本文件
12
6.1.2 预定义的I/O流类概述(续) ——面向字符的流
输入/输出流 阴影部分为节点流
13
6.1.2 预定义的I/O流类概述(续) ——面向字节的流
输入/输出流 数据源或目标中含有非字符数据,必须用字节流来输入/输出 通常被用来读写诸如图片、声音之类的二进制数据 绝大多数数据是被存储为二进制文件的,世界上的文本文件大约只能占到2%,通常二进制文件要比含有相同数据量的文本文件小得多
14
6.1.2 预定义的I/O流类概述(续) ——面向字节的流
输入/输出流 InputStream和OutputStream 是用来处理8位字节流的抽象基类,程序使用这两个类的子类来读写8位的字节信息 分为两部分 节点流 处理流
15
6.1.2 预定义的I/O流类概述(续) ——面向字节的流
输入/输出流 阴影部分为节点流
16
6.1.2 预定义的I/O流类概述(续) ——标准输入输出
输入/输出流 标准输入输出流对象 System类的静态成员变量 包括 System.in: InputStream类型的,代表标准输入流,这个流是已经打开了的,默认状态对应于键盘输入。 System.out:PrintStream类型的,代表标准输出流,默认状态对应于屏幕输出 System.err:PrintStream类型的,代表标准错误信息输出流,默认状态对应于屏幕输出
17
6.1.2 预定义的I/O流类概述(续) ——标准输入输出
输入/输出流 标准I/O重新导向 setIn(InputStream): 设置标准输入流 setOut(PrintStream):设置标准输出流 setErr(PrintStream):设置标准错误输出流
18
6.1.2 预定义的I/O流类概述(续) ——例6_1 输入/输出流 从键盘读入信息并在显示器上显示 运行结果 Hello!
import java.io.*; public class Echo { public static void main(String[] args) throws IOException { BufferedReader in = new BufferedReader( new InputStreamReader(System.in)); String s; while((s = in.readLine()).length() != 0) System.out.println(s); } 运行结果 Hello!
19
6.1.2 预定义的I/O流类概述(续) ——例6_1说明
输入/输出流 System.in 程序启动时由Java系统自动创建的流对象,它是原始的字节流,不能直接从中读取字符,需要对其进行进一步的处理 InputStreamReader(System.in) 以System.in为参数创建一个InputStreamReader流对象,相当于字节流和字符流之间的一座桥梁,读取字节并将其转换为字符 BufferedReader in 对InputStreamReader处理后的信息进行缓冲,以提高效率
20
Java SE 5.0新特性 Java 5.0终于也有了自己的printf! 一个方便的扫描API: 把文本转化成基本类型或者String
out.printf(“%-12s is %2d long”, name, l); out.printf(“value = %2.2F”, value); %n 是平台无关的换行标志 一个方便的扫描API: 把文本转化成基本类型或者String Scanner s = new Scanner(System.in); int n = s.nextInt(); 还有下列方法:next.Byte(),nextDouble(),nextFloat,nextInt(), nextLine(),nextLong(),nextShort()
21
6.1.2 预定义的I/O流类概述(续) ——Redirecting.java
输入/输出流 重导向标准输入System.in和标准输出System.out import java.io.*; public class Redirecting { public static void main(String[] args) throws IOException { BufferedInputStream in = new BufferedInputStream( new FileInputStream( "Redirecting.java")); PrintStream out = new PrintStream( new BufferedOutputStream( new FileOutputStream("test.out"))); System.setIn(in); System.setOut(out); System.setErr(out); BufferedReader br = new BufferedReader( new InputStreamReader(System.in)); String s; while((s = br.readLine()) != null) System.out.println(s); out.close(); // Remember this! }
22
6.1.2 预定义的I/O流类概述(续) ——处理流 处理流 输入/输出流 不直接与数据源或目标相连,而是基于另一个流来构造
从流读写数据的同时对数据进行处理 例6-1中的InputStreamReader和BufferedReader都属于处理流 InputStreamReader读取字节并转换为字符 BufferedReader对另一个流产生的数据进行缓冲
23
6.1.2 预定义的I/O流类概述(续) ——处理流 输入/输出流 用一行表达式实现:
用一行表达式实现: BufferedReader stdin = new BufferedReader ( new InputStreamReader(System.in) );
24
6.1.2 预定义的I/O流类概述(续) ——I/O异常
输入/输出流 IO异常 多数IO方法在遇到错误时会抛出异常,因此调用这些方法时必须 在方法头声明抛出IOException异常 或者在try块中执行IO,然后捕获IOException
25
6.2 文件读写 写文本文件 读文本文件 写二进制文件 读二进制文件 File类 处理压缩文件 对象序列化 随机文件读写
26
6.2.1 写文本文件 本节知识点 文件读写 FileWriter类 创建一个磁盘文件 关闭一个磁盘文件 write() 方法
捕获I/O异常 BufferedWriter 类
27
6.2.1 写文本文件(续) ——例6_2 文件读写 在C盘根目录创建文本文件Hello.txt,并往里写入若干行文本
6.2.1 写文本文件(续) ——例6_2 文件读写 在C盘根目录创建文本文件Hello.txt,并往里写入若干行文本 import java.io.*; class Ex6_2 { public static void main ( String[] args ) throws IOException { //main方法中声明抛出IO异常 String fileName = "C:\\Hello.txt"; FileWriter writer = new FileWriter( fileName ); writer.write( "Hello!\n"); writer.write( "This is my first text file,\n" ); writer.write( "You can see how this is done.\n" ); writer.write("输入一行中文也可以\n"); writer.close(); }
28
6.2.1 写文本文件(续) ——例6_2运行结果 打开C盘根目录下的Hello.txt文件 文件读写
6.2.1 写文本文件(续) ——例6_2运行结果 文件读写 打开C盘根目录下的Hello.txt文件 换行有些问题,例6_4中将解决这个问题
29
6.2.1 写文本文件(续) ——例6_2说明 每次运行这个程序,都将删除已经存在的”Hello.txt”文件,创建一个新的同名文件
6.2.1 写文本文件(续) ——例6_2说明 每次运行这个程序,都将删除已经存在的”Hello.txt”文件,创建一个新的同名文件 FileWriter的构造方法有五个,本例是通过一个字符串指定文件名来创建 FileWriter类的write方法向文件中写入字符
30
6.2.1 写文本文件(续) ——例6_2说明(续) 文件读写 Writer类的流可实现内部格式到外部磁盘文件格式的转换
6.2.1 写文本文件(续) ——例6_2说明(续) 文件读写 Writer类的流可实现内部格式到外部磁盘文件格式的转换 “Hello.txt”是一个普通的ASCII码文本文件,每个英文字符占一个字节,中文字符占两个字节 Java程序中的字符串则是每个字符占两个字节的,采用Unicode编码 close方法清空流里的内容并关闭它。如果不调用该方法,可能系统还没有完成所有数据的写操作,程序就结束了
31
6.2.1 写文本文件(续) ——例6_3 文件读写 处理IO异常 import java.io.*; class Ex6_3 {
6.2.1 写文本文件(续) ——例6_3 文件读写 处理IO异常 import java.io.*; class Ex6_3 { public static void main ( String[] args ) { String fileName = "c:\\Hello.txt" ; try { //将所有IO操作放入try块中 FileWriter writer = new FileWriter( fileName ,true ); writer.write( "Hello!\n"); writer.write( "This is my first text file,\n" ); writer.write( "You can see how this is done. \n" ); writer.write("输入一行中文也可以\n"); writer.close(); } catch ( IOException iox) { System.out.println("Problem writing" + fileName ); }
32
6.2.1 写文本文件(续) ——例6_3说明 文件读写 运行此程序,会发现在原文件内容后面又追加了重复的内容,这就是将构造方法的第二个参数设为true的效果 如果将文件属性改为只读属性,再运行本程序,就会出现IO错误,程序将转入catch块中,给出出错信息
33
6.2.1 写文本文件(续) ——BufferedWriter类
文件读写 BufferedWriter类 如果需要写入的内容很多,就应该使用更为高效的缓冲器流类BufferedWriter FileWriter和BufferedWriter类都用于输出字符流,包含的方法几乎完全一样,但BufferedWriter多提供了一个newLine()方法用于换行 不同厂家生产的计算机 (IBM, Apple, VAX, Sun) 对文字的换行方法不同。newLine()方法可以输出在当前计算机上正确的换行符
34
6.2.1 写文本文件(续) ——例6_4 文件读写 使用BufferedWriter完成例6-2实现的功能
6.2.1 写文本文件(续) ——例6_4 文件读写 使用BufferedWriter完成例6-2实现的功能 import java.io.*; //ex6_4 class Ex6_4 { public static void main ( String[] args ) throws IOException { String fileName = "C:/newHello.txt" ; BufferedWriter out = new BufferedWriter( new FileWriter( fileName ) ); out.write( "Hello!" ); out.newLine() ; out.write( "This is another text file using BufferedWriter," ); out.newLine(); ; out.write( "So I can use a common way to start a newline" ); out.close(); }
35
6.2.1 写文本文件(续) ——例6_4运行结果 文件读写 用任何文本编辑器打开newHello.txt都会出现正确的换行效果
36
6.2.2 读文本文件 本节知识点 文件读写 Reader FileReader BufferedReader和readLine()
文本文件复制
37
6.2.2 读文本文件(续) FileReader类 BufferedReader 文件读写 从文本文件中读取字符
继承自Reader抽象类的子类InputStreamReader BufferedReader 读文本文件的缓冲器类 具有readLine()方法,可以对换行符进行鉴别,一行一行地读取输入流中的内容 继承自Reader
38
6.2.2 读文本文件(续) 文件读写 文件输入方法: BufferedReader in = new BufferedReader(new FileReader( fileName) );
39
6.2.2 读文本文件(续) ——例6_5 文件读写 从Hello.txt中读取文本并显示在屏幕上 import java.io.*;
6.2.2 读文本文件(续) ——例6_5 文件读写 从Hello.txt中读取文本并显示在屏幕上 import java.io.*; class Ex6_5 { public static void main ( String[] args ) { String fileName = "C:/Hello.txt" , line; try { BufferedReader in = new BufferedReader( new FileReader( fileName ) ); line = in.readLine(); //读取一行内容 while ( line != null ) { System.out.println( line ); line = in.readLine(); } in.close(); catch ( IOException iox ) { System.out.println("Problem reading " + fileName );
40
6.2.2 读文本文件(续) ——例6_5说明 文件读写 运行该程序,屏幕上将逐行显示出Hello.txt文件中的内容
6.2.2 读文本文件(续) ——例6_5说明 文件读写 运行该程序,屏幕上将逐行显示出Hello.txt文件中的内容 FileReader对象:创建后将打开文件,如果文件不存在,会抛出一个IOException BufferedReader类的readLine()方法:从一个面向字符的输入流中读取一行文本。如果其中不再有数据,返回null Reader类的read()方法:也可用来判别文件结束。该方法返回的一个表示某个字符的int型整数,如果读到文件末尾,返回 -1。据此,可修改本例中的读文件部分: int c; while((c=in.read())!= -1) System.out.print((char)c); close()方法:为了操作系统可以更为有效地利用有限的资源,应该在读取完毕后,调用该方法
41
6.2.2 读文本文件(续) ——例6_6 指定源文件和目标文件名,将源文件的内容拷贝至目标文件。调用方式为: 文件读写
6.2.2 读文本文件(续) ——例6_6 文件读写 指定源文件和目标文件名,将源文件的内容拷贝至目标文件。调用方式为: java copy sourceFile destinationFile
42
6.2.2 读文本文件(续) ——例6_6 共包括两个类 文件读写 CopyMaker Ex6_6
6.2.2 读文本文件(续) ——例6_6 文件读写 共包括两个类 CopyMaker private boolean openFiles() private boolean copyFiles() private boolean closeFiles() public boolean copy(String src, String dst ) Ex6_6 main()
43
6.2.2 读文本文件(续) ——例6_6 import java.io.*; class CopyMaker {
6.2.2 读文本文件(续) ——例6_6 import java.io.*; class CopyMaker { String sourceName, destName; BufferedReader source; BufferedWriter dest; String line;
44
6.2.2 读文本文件(续) ——例6_6 文件读写 private boolean openFiles() { try {
6.2.2 读文本文件(续) ——例6_6 文件读写 private boolean openFiles() { try { source = new BufferedReader(new FileReader( sourceName )); } catch ( IOException iox ) { System.out.println("Problem opening " + sourceName ); return false; dest = new BufferedWriter(new FileWriter( destName )); catch ( IOException iox ) { System.out.println("Problem opening " + destName ); return true;
45
6.2.2 读文本文件(续) ——例6_6 文件读写 private boolean copyFiles() { try {
6.2.2 读文本文件(续) ——例6_6 文件读写 private boolean copyFiles() { try { line = source.readLine(); while ( line != null ) { dest.write(line); dest.newLine(); } catch ( IOException iox ) { System.out.println("Problem reading or writing" ); return false; return true;
46
6.2.2 读文本文件(续) ——例6_6 文件读写 private boolean closeFiles() {
6.2.2 读文本文件(续) ——例6_6 文件读写 private boolean closeFiles() { boolean retVal=true; try { source.close(); } catch ( IOException iox ) { System.out.println("Problem closing " + sourceName ); retVal = false; } try { dest.close(); } System.out.println("Problem closing " + destName ); return retVal;
47
6.2.2 读文本文件(续) ——例6_6 文件读写 public boolean copy(String src, String dst ) { sourceName = src ; destName = dst ; return openFiles() && copyFiles() && closeFiles(); } public class Ex6_6 //一个文件中只能有一个公有类 { public static void main ( String[] args ) { if ( args.length == 2 ) new CopyMaker().copy(args[0], args[1]); else System.out.println("Please Enter File names");
48
6.2.2 读文本文件(续) ——例6_6运行结果 文件读写
6.2.2 读文本文件(续) ——例6_6运行结果 文件读写 此文件Ex6_6.java编译后生成Ex6_6.class和CopyMaker.class两个字节码文件 运行结果 在命令行方式下执行如下命令 java Ex6_6 c:/Hello.txt c:/CopyHello.txt 则在C盘根目录下会出现CopyHello.txt文件,内容与Hello.txt完全相同
49
6.2.3 写二进制文件 本节知识点 文件读写 二进制文件 OutputStream FileOutputStream
BufferedOutputStream DataOutputStream writeInt() writeDouble() writeBytes()
50
6.2.3 写二进制文件(续) ——二进制文件 文件读写 二进制文件 原则上讲,所有文件都是由8位的字节组成的
6.2.3 写二进制文件(续) ——二进制文件 文件读写 二进制文件 原则上讲,所有文件都是由8位的字节组成的 如果文件字节中的内容应被解释为字符,则文件被称为文本文件;如果被解释为其它含义,则文件被称为二进制文件 例如文字处理程序,例如字处理软件Word产生的doc文件中,数据要被解释为字体、格式、图形和其他非字符信息。因此,这样的文件是二进制文件,不能用Reader流正确读取
51
6.2.3 写二进制文件(续) ——二进制文件 为什么需要二进制文件 输入输出更快 比文本文件小很多 有些数据不容易被表示为字符
52
6.2.3 写二进制文件(续) ——OutputStream类
文件读写 抽象类OutputStream 派生类FileOutputStream 用于一般目的输出(非字符输出) 用于成组字节输出 派生类DataOutputStream 具有写各种基本数据类型的方法 将数据写到另一个输出流 它在所有的计算机平台上使用同样的数据格式 其常用的一些方法见表6-2 其中size方法,可作为计数器,统计写入的字节数
53
6.2.3 写二进制文件(续) ——表6_2 文件读写
54
6.2.3 写二进制文件(续) ——表6_2 文件读写
55
6.2.3 写二进制文件(续) ——例6_7 文件读写 将三个int型数字255/0/-1写入数据文件data1.dat
6.2.3 写二进制文件(续) ——例6_7 文件读写 将三个int型数字255/0/-1写入数据文件data1.dat import java.io.*; class Ex6_7{ public static void main ( String[] args ) { String fileName = "c:/data1.dat" ; int value0 = 255, value1 = 0, value2 = -1; try { DataOutputStream out = new DataOutputStream( new FileOutputStream( fileName ) ); out.writeInt( value0 ); out.writeInt( value1 ); out.writeInt( value2 ); out.close(); } catch ( IOException iox ){ System.out.println("Problem writing " + fileName ); }
56
6.2.3 写二进制文件(续) ——例6_7运行结果 文件读写 运行结果 说明 运行程序后,在C盘生成数据文件data1.dat
6.2.3 写二进制文件(续) ——例6_7运行结果 UltraEdit-32是一套极棒的文字、Hex、ASCII码编辑器,可以取代记事本,内建英文单字检查、C++及VB指令突显,可同时编辑多个文件,而且即使开启很大的文件速度也不会慢。其并且附有HTMLTag颜色显示、搜寻替换以及无限制的还原功能;一般大家常会用其来修改EXE或DLL文件。 文件读写 运行结果 运行程序后,在C盘生成数据文件data1.dat 用写字板打开没有任何显示 用ultraEdit打开查看其二进制信息,内容为 FF FF FF FF FF,每个int数字都是32个bit的 说明 FileOutputStream类的构造方法负责打开文件“data1.dat”用于写数据 FileOutputStream类的对象与DataOutputStream对象连接,写基本类型的数据
57
6.2.3 写二进制文件(续) ——BufferedOutputStream类
文件读写 BufferedOutputStream 写二进制文件的缓冲流类 类似于文本文件中的BufferedWriter 对于大量数据的写入,可提高效率 用法示例: DataOutputStream out = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( fileName ) ) );
58
6.2.3 写二进制文件(续) ——例6_8 文件读写 向文件中写入各种数据类型的数,并统计写入的字节数 import java.io.*;
6.2.3 写二进制文件(续) ——例6_8 文件读写 向文件中写入各种数据类型的数,并统计写入的字节数 import java.io.*; class Ex6_8{ public static void main ( String[] args ) throws IOException { String fileName = "mixedTypes.dat" ; DataOutputStream dataOut = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( fileName ) ) ); dataOut.writeInt( 0 ); System.out.println( dataOut.size() + " bytes have been written."); dataOut.writeDouble( 31.2 ); dataOut.writeBytes("JAVA"); dataOut.close(); }
59
6.2.3 写二进制文件(续) ——例6_8运行结果 运行结果 说明 文件读写 4 bytes have been written
6.2.3 写二进制文件(续) ——例6_8运行结果 文件读写 运行结果 4 bytes have been written 12 bytes have been written 16 bytes have been written 说明 这个程序可作为字节计数器
60
6.2.3 写二进制文件(续) ——例6_9 文件读写 向文件中写入内容为-1的一个字节,并读出。 import java.io.*;
6.2.3 写二进制文件(续) ——例6_9 文件读写 向文件中写入内容为-1的一个字节,并读出。 import java.io.*; public class Ex6_9 { public static void main(String[] args) throws Exception { DataOutputStream out=new DataOutputStream( new FileOutputStream("c:/trytry.dat")); out.writeByte(-1); out.close(); DataInputStream in=new DataInputStream( new FileInputStream("c:/trytry.dat")); int a=in.readByte(); System.out.println(Integer.toHexString (a)); System.out.println(a); in.skip (-1); //往后一个位置,以便下面重新读出 a=in.readUnsignedByte(); System.out.println(a); in.close(); }
61
6.2.3 写二进制文件(续) ——例6_9运行结果 文件读写 运行结果 说明 ffffffff -1 ff 255
6.2.3 写二进制文件(续) ——例6_9运行结果 文件读写 运行结果 ffffffff -1 ff 255 说明 用ultraEdit打开c:/trytry.dat文件,其内容为FF 如果用readByte读入,其高24位都将补1,所以结果还是-1 如果用readUnsignedByte读入,其高24位都将补0,结果就变成了255 写的字节是连续的,中间没有分隔符,所以应该记住写的数据类型、个数等情况,以便将来利用
62
6.2.4 读二进制文件 本节知识点 文件读写 FileInputStream DataInputStream
BufferedInputSteam 读写整数 读写单字节
63
6.2.4 读二进制文件(续) ——过滤流 过滤流 文件读写 读或写的同时对数据进行处理 通过另外一个流来构造一个过滤流
6.2.4 读二进制文件(续) ——过滤流 文件读写 过滤流 读或写的同时对数据进行处理 通过另外一个流来构造一个过滤流 大部分java.io 包所提供过滤流都是FilterInputStream和FilterOutputStream的子类 DataInputStream 和 DataOutputStream BufferedInputStream 和 BufferedOutputStream LineNumberInputStream PushbackInputStream PrintStream
64
6.2.4 读二进制文件(续) ——例6_10 文件读写 读取例6-7创建的数据文件中的3个int型数字,显示相加结果
6.2.4 读二进制文件(续) ——例6_10 文件读写 读取例6-7创建的数据文件中的3个int型数字,显示相加结果 import java.io.*; class Ex6_10 { public static void main ( String[] args ) { String fileName = "c:\\data1.dat"; int sum = 0; try { DataInputStream instr = new DataInputStream( new BufferedInputStream(new FileInputStream( fileName ))); sum += instr.readInt(); System.out.println( "The sum is: " + sum ); instr.close(); } catch ( IOException iox ) { System.out.println("Problem reading " + fileName ); }
65
6.2.4 读二进制文件(续) ——例6_10运行结果 该程序显示结果是254 文件读写 分析
6.2.4 读二进制文件(续) ——例6_10运行结果 文件读写 该程序显示结果是254 分析 readInt方法可以从输入流中读入4个字节并将其当作int型数据 由于知道文件中存储的是3个int型数据,所以使用了3个读入语句 如果不知道数据的个数该怎么办呢?因为DataInputStream的读入操作如遇到文件结尾就会抛出EOFException异常,所以我们可以将读操作放入try块中
66
6.2.4 读二进制文件(续) ——例6_10修改 文件读写 将读操作放入try块中,使遇到文件结尾就会抛出EOFException异常,进入到相应的catch块中 try { while ( true ) sum += instr.readInt(); } catch ( EOFException eof ) System.out.println( "The sum is: " + sum ); instr.close();
67
6.2.4 读二进制文件(续) ——例6_10修改 文件读写 如果没有读到结尾,在读取过程中发生的异常属于IOException,这样就需要我们再加一个catch块处理这种异常 一个try块后面可以跟不止一个catch块,用于处理各种可能发生的异常 我们可以在上段代码后再加上用于捕捉IOException的代码段如下 catch ( IOException eof ) { System.out.println( "Problem reading input" ); instr.close(); }
68
6.2.4 读二进制文件(续) ——例6_10修改 如果catch块中的close方法也发生异常,现在就没法捕获了。解决方法可以有 文件读写
6.2.4 读二进制文件(续) ——例6_10修改 文件读写 如果catch块中的close方法也发生异常,现在就没法捕获了。解决方法可以有 在main方法中抛出异常 比较简单 缺点是没有catch块,因而无法对异常进行进一步处理,例如给出提示信息 使用嵌套的try块
69
6.2.4 读二进制文件(续) ——例6_11 文件读写 import java.io.*; class Ex6_11{
6.2.4 读二进制文件(续) ——例6_11 文件读写 import java.io.*; class Ex6_11{ public static void main ( String[] args ) { String fileName = "c:/data1.dat" ; long sum = 0; try { DataInputStream instr = new DataInputStream( new BufferedInputStream(new ileInputStream(fileName))); while ( true ) sum += instr.readInt(); } catch ( EOFException eof ) { System.out.println( "The sum is: " + sum ); instr.close(); catch ( IOException iox ) { System.out.println("IO Problems with " + fileName ); }
70
6.2.4 读二进制文件(续) ——例6_12 文件读写 由于文本文件的存储方式其实也是二进制代码,因此也可使用InputStream类的方法读取 用InputStream类读取文本文件并打印在屏幕上 import java.io.*; public class Ex6_12 { public static void main(String[] args) throws IOException { FileInputStream s=new FileInputStream("c:/Hello.txt"); int c; while ((c = s.read()) != -1) //读取1字节,结束返回-1 System.out.write(c); s.close(); }
71
6.2.4 读二进制文件(续) ——读写字节 文件读写 DataOutputStream的writeByte方法
6.2.4 读二进制文件(续) ——读写字节 文件读写 DataOutputStream的writeByte方法 public final void writeByte(int b) throws IOException 将int的最不重要字节写入输出流 DataInputStream的readUnsignedByte方法 public final int readUnsignedByte() throws IOException 从输入流中读取1字节存入int的最不重要字节
72
6.2.4 读二进制文件(续) ——文件复制程序 文件读写 从命令行输入源文件名和目标文件名,将源文件复制为目标文件。
6.2.4 读二进制文件(续) ——文件复制程序 文件读写 从命令行输入源文件名和目标文件名,将源文件复制为目标文件。 import java.io.*; class CopyBytes { public static void main ( String[] args ) { DataInputStream instr; DataOutputStream outstr; if ( args.length != 2 ) { System.out.println("Please enter file names"); return; } try { instr = new DataInputStream(new BufferedInputStream(new FileInputStream( args[0] ))); outstr = new DataOutputStream(new BufferedOutputStream(new FileOutputStream( args[1] )));
73
6.2.4 读二进制文件(续) ——文件复制程序 文件读写 try { int data; while ( true ) {
6.2.4 读二进制文件(续) ——文件复制程序 文件读写 try { int data; while ( true ) { data = instr.readUnsignedByte() ; outstr.writeByte( data ) ; } catch ( EOFException eof ) { outstr.close(); instr.close(); return; catch ( FileNotFoundException nfx ) { System.out.println("Problem opening files" ); } catch ( IOException iox ) { System.out.println("IO Problems" ); }
74
6.2.5 File 类 表示磁盘文件信息 文件读写 定义了一些与平台无关的方法来操纵文件 构造文件流可以使用File类的对象作为参数
创建、删除文件 重命名文件 判断文件的读写权限及是否存在 设置和查询文件的最近修改时间等 构造文件流可以使用File类的对象作为参数
75
6.2.5 File类(续) ——构造方法 文件读写
76
6.2.5 File类(续) ——常用方法 文件读写
77
6.2.5 File类(续) ——例6_13 文件读写 例6_13 在C盘创建文件Hello.txt,如果存在则删除旧文件,不存在则直接创建新的 import java.io.*; public class Ex6_13 { public static void main(String[] args) { File f=new File("c:"+File.separator+"Hello.txt"); if (f.exists()) f.delete(); else try{ f.createNewFile(); } catch(Exception e){ System.out.println(e.getMessage());
78
6.2.5 File类(续) ——例6_13运行结果 运行结果 文件读写 分析
因为在例6_2中已经创建了c:\Hello.txt,所以第一次运行将删除这个文件 第二次运行则又创建了一个此名的空文件 分析 在试图打开文件之前,可以使用File类的isFile方法来确定File对象是否代表一个文件而非目录) 还可通过exists方法判断同名文件或路径是否存在,进而采取正确的方法,以免造成误操作
79
6.2.5 File类(续) ——改进的文件复制程序 文件读写 import java.io.*; class NewCopyBytes{
public static void main ( String[] args ) { DataInputStream instr; DataOutputStream outstr; if ( args.length != 2 ) { System.out.println("Please Enter file names!"); return; } File inFile = new File( args[0] ); File outFile = new File( args[1] ); if ( outFile.exists() ) { System.out.println( args[1] + " already exists"); if ( !inFile.exists() ) { System.out.println( args[0] + " does not exist");
80
6.2.5 File类(续) ——改进的文件复制程序 文件读写 try{
instr = new DataInputStream(new BufferedInputStream( new FileInputStream( inFile ))); outstr = new DataOutputStream(new BufferedOutputStream( new FileOutputStream( outFile ))); try { int data; while ( true ) { data = instr.readUnsignedByte() ; outstr.writeByte( data ) ; } } catch ( EOFException eof ) { outstr.close(); instr.close(); return; } catch ( FileNotFoundException nfx ) { System.out.println("Problem opening files" ); } catch ( IOException iox ) { System.out.println("IO Problems" ); }
81
6.2.6 处理压缩文件 压缩流类 文件读写 java.util.zip包中提供了一些类,使我们可以以压缩格式对流进行读写
它们都继承自字节流类OutputStream和InputStream 其中GZIPOutputStream和ZipOutputStream可分别把数据压缩成GZIP格式和Zip格式 GZIPInputStream和ZipInputStream可以分别把压缩成GZIP格式或Zip的数据解压缩恢复原状
82
6.2.6 处理压缩文件(续) ——简单的GZIP压缩格式
文件读写 GZIPOutputStream 父类是DeflaterOutputStream 可以把数据压缩成GZIP格式 GZIPInputStream 父类是 InflaterInputStream 可以把压缩成GZIP格式的数据解压缩
83
6.2.6 处理压缩文件(续) ——例6_14 文件读写 将例6_1创建的文本文件“Hello.txt” 压缩为文件“test.gz”,再解压该文件,显示其中内容,并另存为“newHello.txt” import java.io.*; import java.util.zip.*; public class Ex6_14 { public static void main(String[] args) throws IOException { FileInputStream in = new FileInputStream("c:/Hello.txt"); GZIPOutputStream out = new GZIPOutputStream( new FileOutputStream("c:/test.gz")); System.out.println("Writing compressing file from c:/Hello.txt to c:/test.gz"); int c; while((c = in.read()) != -1) out.write(c); //写压缩文件
84
6.2.6 处理压缩文件(续) ——例6_14 文件读写 in.close(); out.close();
6.2.6 处理压缩文件(续) ——例6_14 文件读写 in.close(); out.close(); System.out.println("Reading file form c:/test.gz to monitor"); BufferedReader in2 = new BufferedReader( new InputStreamReader( new GZIPInputStream( new FileInputStream("c:/test.gz")))); String s; while((s = in2.readLine()) != null) System.out.println(s); in2.close(); System.out.println("Writing decompression to c:/newHello.txt"); GZIPInputStream in3=new GZIPInputStream( new FileInputStream("c:/test.gz")); FileOutputStream out2=new FileOutputStream("c:/newHello.txt"); while((c=in3.read())!=-1) out2.write(c); in3.close(); out2.close(); }
85
6.2.6 处理压缩文件(续) ——例6_14运行结果 文件读写 运行结果 说明 首先生成了压缩文件“test.gz”
6.2.6 处理压缩文件(续) ——例6_14运行结果 文件读写 运行结果 首先生成了压缩文件“test.gz” 再读取显示其中的内容,和“Hello.txt”中的内容完全一样 解压缩文件“newHello.txt”,和“Hello.txt”中的内容也完全相同 说明 read()方法读取一个字节,转化为[0 ,255]的之间的一个整数,返回一个int。如果读到了文件末尾,则返回-1。 write(int)方法写一个字节的低8位,忽略了高24位。
86
6.2.6 处理压缩文件(续) ——运用ZIP压缩多个文件
文件读写 Zip文件 可能含有多个文件,所以有多个入口(Entry) 每个入口用一个ZipEntity对象表示,该对象的getName()方法返回文件的最初名称 ZipOutputStream 父类是DeflaterOutputStream 可以把数据压缩成ZIP格式 ZipInputStream 父类是InflaterInputStream 可以把压缩成ZIP格式的数据解压缩
87
6.2.6 处理压缩文件(续) ——例6_15 文件读写 从命令行输入若干个文件名,将所有文件压缩为“c:/test.zip”,再从此压缩文件中解压并显示 import java.io.*; import java.util.*; import java.util.zip.*; public class Ex6_15 { public static void main(String[] args) throws IOException { ZipOutputStream out=new ZipOutputStream( new BufferedOutputStream( new FileOutputStream("c:/test.zip")));
88
6.2.6 处理压缩文件(续) ——例6_15 文件读写 for(int i = 0; i < args.length; i++) { System.out.println("Writing file " + args[i]); BufferedInputStream in =new BufferedInputStream( new FileInputStream(args[i])); out.putNextEntry(new ZipEntry(args[i])); int c; while((c = in.read()) != -1) out.write(c); in.close(); } out.close();
89
6.2.6 处理压缩文件(续) ——例6_15 文件读写 System.out.println("Reading file");
6.2.6 处理压缩文件(续) ——例6_15 文件读写 System.out.println("Reading file"); ZipInputStream in2 =new ZipInputStream( new BufferedInputStream( new FileInputStream("c:/test.zip"))); ZipEntry ze; while((ze = in2.getNextEntry()) != null) { System.out.println("Reading file " + ze.getName()); int x; while((x = in2.read()) != -1) System.out.write(x); System.out.println(); } in2.close();
90
6.2.6 处理压缩文件(续) ——例6_15运行结果 运行结果 文件读写 在命令行输入两个文本文件名后,将生成c:/test.zip文件
6.2.6 处理压缩文件(续) ——例6_15运行结果 文件读写 运行结果 在命令行输入两个文本文件名后,将生成c:/test.zip文件 在屏幕上显示出解压后每个文件的内容 在资源管理器窗口中,使用winzip软件可解压缩该文件,恢复出和原来文件相同的两个文本文件
91
6.2.6 处理压缩文件(续) ——例6_16 文件读写 解压缩Zip文件,并恢复其原来路径 import java.util.*;
6.2.6 处理压缩文件(续) ——例6_16 文件读写 解压缩Zip文件,并恢复其原来路径 import java.util.*; import java.util.zip.*; import java.lang.*; import java.io.*; class Unzip { byte doc[]=null; //存储解压缩数据的缓冲字节数组 String Filename=null; //压缩文件名字符串 String UnZipPath=null; //解压缩路径字符串 public Unzip(String filename,String unZipPath) { this.Filename=filename; this.UnZipPath=unZipPath; this.setUnZipPath (this.UnZipPath); } public Unzip(String filename) { this.Filename=new String(filename); this.UnZipPath=null;
92
6.2.6 处理压缩文件(续) ——例6_16 private void setUnZipPath(String unZipPath) {
6.2.6 处理压缩文件(续) ——例6_16 文件读写 private void setUnZipPath(String unZipPath) { if(unZipPath.endsWith("\\")) this.UnZipPath=new String(unZipPath); else this.UnZipPath=new String(unZipPath+"\\"); } public void doUnZip() { try { ZipInputStream zipis=new ZipInputStream( new FileInputStream(Filename)); ZipEntry fEntry=null; while((fEntry=zipis.getNextEntry())!=null) { if (fEntry.isDirectory()) //是路径则创建路径 checkFilePath(UnZipPath+fEntry.getName());
93
6.2.6 处理压缩文件(续) ——例6_16 文件读写 else { //是文件则解压缩文件
6.2.6 处理压缩文件(续) ——例6_16 文件读写 else { //是文件则解压缩文件 String fname=new String(UnZipPath+fEntry.getName()); try{ FileOutputStream out = new FileOutputStream(fname); doc=new byte[512]; int n; while ((n = zipis.read(doc,0,512)) != -1) out.write(doc, 0, n); out.close(); out=null; doc=null; } catch (Exception ex) { } zipis.close(); //关闭输入流 catch(IOException ioe) { System.out.println(ioe); }
94
6.2.6 处理压缩文件(续) ——例6_16 文件读写 private void checkFilePath(String dirName) throws IOException { File dir = new File(dirName); if(!dir.exists()) dir.mkdirs(); } //主类,用于输入参数,生成Unzip类的实例 public class Ex6_16{ public static void main(String [] args) { String zipFile=args[0]; //第一个参数为zip文件名 String unZipPath=args[1]+"\\"; //第二个参数为指定解压缩路径 Unzip myZip=new Unzip(zipFile,unZipPath); myZip.doUnZip();
95
6.2.6 处理压缩文件(续) ——例6_16说明 Unzip类 文件读写 主类Ex6_16
6.2.6 处理压缩文件(续) ——例6_16说明 文件读写 Unzip类 用来将指定zip文件解压到指定路径下 主类Ex6_16 main方法中,我们可以从命令行输入zip文件名和解压缩路径 可以在资源管理器下用winzip等软件将若干文件(包括目录,子文件夹等)压缩为zip文件,试验一下这个程序,将得到和winzip软件解压缩相同的效果
96
6.2.7 对象序列化 保存对象的信息,在需要的时候,再读取这个对象 文件读写 内存中的对象在程序结束时就会被垃圾回收机制清除
用于对象信息存储和读取的输入输出流类: ObjectInputStream ObjectOutputStream
97
6.2.7 对象序列化(续) ——ObjectInputStream/ObjectOutputStream类
文件读写 ObjectInputStream和ObjectOutputStream 实现对象的读写 通过ObjectOutputStream把对象写入磁盘文件 通过ObjectInputStream把对象读入程序 不保存对象的transient和static类型的变量 对象要想实现序列化,其所属的类必须实现Serializable接口
98
6.2.7 对象序列化(续) ——写入ObjectOutputStream
文件读写 必须通过另一个流构造ObjectOutputStream: FileOutputStream out = new FileOutputStream("theTime"); ObjectOutputStream s = new ObjectOutputStream(out); s.writeObject("Today"); s.writeObject(new Date()); s.flush();
99
6.2.7 对象序列化(续) ——用ObjectInputStream读入
文件读写 必须通过另一个流构造ObjectInputStream: FileInputStream in = new FileInputStream("theTime"); ObjectInputStream s = new ObjectInputStream(in); String today = (String)s.readObject(); Date date = (Date)s.readObject();
100
6.2.7 对象序列化(续) ——Seriealizable接口
文件读写 空接口,使类的对象可实现序列化 Serializable 接口的定义 package java.io; public interface Serializable { // there's nothing in here! }; 实现Serializable接口的语句 public class MyClass implements Serializable { ... } 使用关键字transient可以阻止对象的某些成员被自动写入文件
101
6.2.7 对象序列化(续) ——例6_17 文件读写 创建一个书籍对象,并把它输出到一个文件book.dat中,然后再把该对象读出来,在屏幕上显示对象信息 class Book implements Serializable { int id; String name; String author; float price; public Book(int id,String name,String author,float price) { this.id=id; this.name=name; this.author=author; this.price=price; }
102
6.2.7 对象序列化(续) ——例6_17 文件读写 import java.io.*; public class Ex6_17 {
6.2.7 对象序列化(续) ——例6_17 文件读写 import java.io.*; public class Ex6_17 { public static void main(String args[]) throws IOException,ClassNotFoundException { Book book=new Book(100032,"Java Programming Skills","Wang Sir",30); ObjectOutputStream oos=new ObjectOutputStream( new FileOutputStream("c:/book.dat")); oos.writeObject(book); oos.close();
103
6.2.7 对象序列化(续) ——例6_17 文件读写 book=null;
6.2.7 对象序列化(续) ——例6_17 文件读写 book=null; ObjectInputStream ois=new ObjectInputStream( new FileInputStream("c:/book.dat")); book=(Book)ois.readObject(); ois.close(); System.out.println("ID is:"+book.id); System.out.println("name is:"+book.name); System.out.println("author is:"+book.author); System.out.println("price is:"+book.price); }
104
6.2.7 对象序列化(续) ——例6_17运行结果 文件读写 运行结果 说明 将生成book.dat文件,并在屏幕显示:
6.2.7 对象序列化(续) ——例6_17运行结果 文件读写 运行结果 将生成book.dat文件,并在屏幕显示: ID is:100032 name is:Java Programming Skills author is:Wang Sir price is:30.0 说明 如果希望增加Book类的功能,使其还能够具有借书方法borrowBook,并保存借书人的借书号borrowerID,可对Book类添加如下内容: transient int borrowerID; public void borrowBook(int ID){ this.borrowerID=ID; }
105
6.2.7 对象序列化(续) ——例6_17修改 文件读写 在main方法中创建了Book类的一个对象后,紧接着调用borrowBook方法
6.2.7 对象序列化(续) ——例6_17修改 文件读写 在main方法中创建了Book类的一个对象后,紧接着调用borrowBook方法 book.borrowBook(2018); 从读入的对象中输出borrowerID System.out.println("Borrower ID is:"+book.borrowerID); 运行结果 显示borrrowID为0,因为声明为transient,所以不保存 如果去掉transient关键子,则可以正确读出2018。这对于保护比较重要的信息(例如密码等)是很有必要的
106
6.2.7 对象序列化(续) ——Externalizable接口
文件读写 Externalizable 接口 实现该接口可以控制对象的读写 API中的说明为 public interface Externalizable extends Serializable 其中有两个方法writeExternal()和readExternal(),因此实现该接口的类必须实现这两个方法 ObjectOutputStream的writeObject()方法只写入对象的标识,然后调用对象所属类的writeExternal() ObjectInputStream的readObject()方法调用对象所属类的readExternal()
107
6.2.7 对象序列化(续) ——Thinking in Java c11:Blip3.java
文件读写 import java.io.*; import java.util.*; public class Blip3 implements Externalizable { int i; String s; public Blip3() { System.out.println("Blip3 Constructor"); } public Blip3(String x, int a) { System.out.println("Blip3(String x, int a)"); s = x; i = a; } public String toString() { return s + i; }
108
6.2.7 对象序列化(续) ——Thinking in Java c11:Blip3.java
文件读写 public void writeExternal(ObjectOutput out) throws IOException{ System.out.println("Blip3.writeExternal"); // You must do this: out.writeObject(s); out.writeInt(i); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { System.out.println("Blip3.readExternal"); s = (String)in.readObject(); i =in.readInt();
109
6.2.7 对象序列化(续) ——Thinking in Java c11:Blip3.java
文件读写 public static void main(String[] args) throws IOException, ClassNotFoundException { System.out.println("Constructing objects:"); Blip3 b3 = new Blip3("A String ", 47); System.out.println(b3); ObjectOutputStream o = new ObjectOutputStream( new FileOutputStream("Blip3.out")); System.out.println("Saving object:"); o.writeObject(b3); o.close(); // Now get it back: ObjectInputStream in = new ObjectInputStream( new FileInputStream("Blip3.out")); System.out.println("Recovering b3:"); b3 = (Blip3)in.readObject(); }
110
6.2.8 随机文件读写 文件读写 随机文件读写 对于很多场合,例如银行系统、实时销售系统,要求能够迅速、直接地访问文件中的特定信息,而无需查找其他的记录。这种类型的即时访问可能要用到随机存取文件和数据库 随机文件的应用程序必须指定文件的格式。最简单的是要求文件中的所有记录均保持相同的固定长度。利用固定长度的记录,程序可以容易地计算出任何一条记录相对于文件头的确切位置 Java.io包提供了RandomAccessFile类用于随机文件的创建和访问
111
6.2.8 随机文件读写(续) ——RandomAccessFile类
可跳转到文件的任意位置读/写数据 可在随机文件中插入数据,而不破坏该文件的其他数据 实现了DataInput 和 DataOutput 接口,可使用普通的读写方法 有个位置指示器,指向当前读写处的位置。刚打开文件时,文件指示器指向文件的开头处。对文件指针显式操作的方法有: int skipBytes(int n):把文件指针向前移动指定的n个字节 void seek(long):移动文件指针到指定的位置。 long getFilePointer():得到当前的文件指针。 在等长记录格式文件的随机读取时有很大的优势,但仅限于操作文件,不能访问其它IO设备,如网络、内存映像等
112
6.2.8 随机文件读写(续) ——RandomAccessFile类
可用来实现读和写,构造方法包括 public RandomAccessFile(File file,String mode) throws FileNotFoundException public RandomAccessFile(String name, String mode) 建立一个RandomAccessFile时,要指出你要执行的操作:仅从文件读,还是同时读写 new RandomAccessFile("farrago.txt", "r"); new RandomAccessFile("farrago.txt", "rw");
113
6.2.8 随机文件读写(续) ——RandomAccessFile类常用API
114
6.3 本章小结 本章内容 本章要求 I/O流的概念以及分类 读写文本文件、二进制文件的方法 处理流的概念及用法 File类 压缩流类
对象序列化的常用流类及接口 随机读写文件的流类 本章要求 理解I/O流的概念,掌握其分类 掌握文本文件读写、二进制文件读写、处理流类的概念和用法、对象序列化 掌握File类、压缩流类、随机读写流类 遇到I/O方面的问题,能够自行查阅API文档解决
Similar presentations