二进制IO

来自CloudWiki
Cloud17讨论 | 贡献2018年4月5日 (四) 07:51的版本 (创建页面,内容为“== 二进制I/O == (1)InputStream和OutputStream InputStream和OutputStream是字节流的两个顶层父类,提供了输入流类与输出流类的通用API…”)
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转至: 导航搜索

二进制I/O

(1)InputStream和OutputStream

InputStream和OutputStream是字节流的两个顶层父类,提供了输入流类与输出流类的通用API。如图6-10所示列出了一些实现二进制I/O的类。

Java6-10.png

图6-10 二进制I/O类

InputStream是输入字节数据用的类,所以InputStream类提供了3种重载的read方法。Inputstream类中的常用方法如表6-3所示。

表6-3 Inputstream类常用方法

方法名	说明
public abstract int read( )	读取一个byte的数据,返回值是高位补0的int类型值。若返回值=-1说明没有读取到任何字节读取工作结束。
public int read(byte b[ ])	读取b.length个字节的数据放到b数组中。返回值是读取的字节数。该方法实际上是调用下一个方法实现的 
public int read(byte b[ ], int off, int len)	从输入流中最多读取len个字节的数据,存放到偏移量为off的b数组中。 
public int available( )	返回输入流中可以读取的字节数。注意若输入阻塞,当前线程将被挂起,如果InputStream对象调用这个方法的话,它只会返回0,这个方法必须由继承InputStream类的子类对象调用才有用,
public long skip(long n)	忽略输入流中的n个字节,返回值是实际忽略的字节数, 跳过一些字节来读取 
public int close( ) 	我们在使用完后,必须对我们打开的流进行关闭.

OutputStream是输出字节数据用的类,它提供了3个write方法来做数据的输出,这个是和InputStream是相对应的。OutputStream常用方法如表6-4所示。

表6-4 Outputstream类常用方法

方法名	说明
public void write(byte b[ ])	将参数b中的字节写到输出流。 
public void write(byte b[ ], int off, int len) 	将参数b的从偏移量off开始的len个字节写到输出流。 
public abstract void write(int b) 	先将int转换为byte类型,把低字节写入到输出流中。 
public void flush( ) 	将数据缓冲区中数据全部输出,并清空缓冲区。
public void close( ) 	关闭输出流并释放与流相关的系统资源。

(2)FileInputStream和FileOutputStream

FileInputStream类和FileOutputStream类用于从/向文件读取/写入字写。他们的方法都是从InputStream类和OutputStream类继承的。

FileInputStream类和FileOutputStream类没有引入新的方法。FileInputStream类构造方法如表6-5所示。

表6-5 FileInputStream类构造方法

方法名	说明
FileInputStream(File file)	通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。 
FileInputStream(String name)	通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。

【注意】如果试图为一个不存在的文件创建FileInputStream对象,将会发生java.io.NotFoundException异常。

要构造FileOutputStream对象,使用如表6-6所示的构造方法。

表6-6 FileOutputStream类常用方法

方法名	说明
FileOutputStream(File file)	使用File对象创建文件输出流对象,如果文件打开失败,将抛出异常。
FileOutputStream(String name)	直接使用文件名或路径创建文件输出流对象。
FileOutputStream(File file, boolean append)	使用File对象创建文件输出流对象,并由参数append指定是否追加文件内容,true为追加,false为不追加。
FileOutputStream(String name, boolean append) 	直接使用文件名或路径创建文件输出流对象,并由参数append指定是否追加。
如果这个文件不存在,就会创建一个新的文件,如果这个文件已经存在,前两个构造方法会删除文件的当前内容。为了在文件内容基础上追加新数据,需要采用后两种构造方法,并将append设置为true。

(3) 输入输出流的操作步骤

  1. 使用引入语句引入java.io包:import java.io.*;
  2. 根据不同数据源和输入输出任务,建立字节流或字符流对象;
  3. 若需要对字节或字符流信息组织加工为数据,在已建字节流或字符流对象上构建数据流对象。
  4. 用输入输出流对象类的成员方法进行读写操作,需要时,设置读写位置指针。
  5. 关闭流对象。

几乎所有的I/O类中的方法都会抛出异常java.io.IOExcepton。因此,必须在方法中声明会抛出java.io.IOExcepton异常,或将代码放到try-catch块中。

下面程序,使用二进制I/O完成了文件的复制。

public static void main(String[] args) throws IOException {
		File srcFile = new File("cache/01_cache.txt"); // 源文件对象
		File destFile = new File("src/desc.txt"); // 目标文件对象
		// 使用源文件对象创建文件输入流对象
		FileInputStream fis = new FileInputStream(srcFile);
		// 使用目标文件对象创建文件输出流对象
		FileOutputStream fos = new FileOutputStream(destFile);
		int hasRead = 0;// 读取字节长度
		byte[] buf = new byte[1024]; // 创建字节数组,作为临时缓冲
		System.out.println("开始复制文件...");
		while ((hasRead = fis.read(buf)) != -1) { // 循环从文件输入流中读取数据
			if (hasRead < 1024)//读取长度<数组长度,复制读取内容
				fos.write(Arrays.copyOf(buf, hasRead)); 
			else
				fos.write(buf);
		}
		System.out.println("文件复制成功!");
		fis.close(); // 关闭流
		fos.close();
}

(4) 使用try-with-resource自动关闭资源。

在编写IO操作时经常会忘记关闭IO流,JDK7提供了新的try-with-resource语法来自动关闭IO流,语法格式如下:

try(声明和创建资源){
	使用资源来处理文件
}

使用try-with-resource语法,重写了复制文件程序中的代码:

public static void main(String[] args) throws IOException {
		File srcFile = new File("cache/01_cache.txt"); // 源文件对象
		File destFile = new File("src/desc.txt"); // 目标文件对象
		// 使用源文件对象创建文件输入流对象
		try (FileInputStream fis = new FileInputStream(srcFile);
				// 使用目标文件对象创建文件输出流对象
				FileOutputStream fos = new FileOutputStream(destFile)) {
			int hasRead = 0;// 读取字节长度
			byte[] buf = new byte[1024]; // 创建字节数组,作为临时缓冲
			System.out.println("开始复制文件...");
			while ((hasRead = fis.read(buf)) != -1) { // 循环从文件输入流中读取数据
				if (hasRead < 1024)// 读取长度<数组长度,复制读取内容
					fos.write(Arrays.copyOf(buf, hasRead));
				else
					fos.write(buf);
			}
			System.out.println("文件复制成功!");
		}
}

【注意】当流不需要使用时,记得使用close()方法将其关闭,或者使用try-with-resource语句自动关闭。不关闭流可能会在输出文件中造成数据受损,或导致其他的程序设计错误。

(5)BufferedInputStream和BufferedOutputStream

BufferedInputStream和 BufferedOutputStream类可以通过减少磁盘读写次数来提高输入和输出的速度。

BufferedInputStream:当向缓冲流写入数据时候,数据先写到缓冲区,待缓冲区写满后,系统一次性将数据发送给输出设备,如图6-11所示。

Java6-11.png

图6-11 BufferedInputStream

若要创建一个BufferedOutputStream流对象,首先需要一个FileOutputStream流对象,然后基于这个流对象创建缓冲流对象。

BufferedOutputStream 类的构造方法如下:

BufferedOutputStream(OutputStream out)
BufferedOutputStream(OutputStream out, int size)

下面程序,使用BufferedOutputStream把内容写入到文件buffer.txt中。

public static void main(String[] args) throws IOException {
		try (FileOutputStream fos = new FileOutputStream("buffer.txt");
				BufferedOutputStream bos = new BufferedOutputStream(fos)) {
			String str = "hello world";
			bos.write(str.getBytes());
		}
}

BufferedOutputStream:当向缓冲流读取数据时候,系统先从缓冲区读出数据,待缓冲区为空时,系统再从输入设备读取数据到缓冲区,如图6-12所示。

Java6-12.png 图6-12 BufferedOutputStream

若要创建一个BufferedInputStream流对象,需要一个FileInputStream流对象,然后基于这个流对象创建缓冲流对象。BufferedInputStream类的构造方法如下:

  • BufferedInputStream(InputStream in)
  • BufferedInputStream(InputStream in, int size)

下面程序,使用BufferedInputStream把读取并打印文件buffer.txt中内容。

public static void main(String[] args) throws  IOException {
		try(FileInputStream fis=new FileInputStream("buffer.txt");
				BufferedInputStream bis=new BufferedInputStream(fis)){
			byte[] bytes=new byte[1024];
			int hasRead=0;
			while((hasRead=bis.read(bytes))!=-1) {
				System.out.println(new String(bytes,0,hasRead));
			}
		}
}

2.文本IO (1)Reader和Writer Java IO中的java.io.Reader和java.io.Writer的工作原理很像InputStream和OutputStream,但不同的是reader和writer是基于字符的,他们是用来用些文本数据的。其类层级结构如图6-13所示。

图6-13 字符流结构 Reader用于读取字符流的抽象类。Reader类的常用方法如表6-7所示。 表6-7 Reader类常用方法 方法名 说明 boolean ready() 输入字符流是否可读 int read() 读取一个字符 int read(char[] cbuf) 读取一串字符(到字符数组cbuf) long skip(long n) 跳过n个字符 mark(int readAheadLimit) 在当前位置做一标记 reset() 将读取位置恢复到标记处 close() 关闭字符流 Writer是写入字符流的抽象类。Writer类的常用方法如表6-8所示。 表6-8 Writer类常用方法 方法名 说明 void close() 关闭流 void flush() 强行写入 void write(int c) 写入c void write(char[] cbuf) 写入字符数组cbuf void write(char[] cbuf, int off, int len) 写入字符数组cbuf中自位置off开始的len个字符 void write(String str) 写入字符串str void write(String str, int off, int len) 写入字符串str中自位置off开始的len个字符 (2)FileReader和FileWriter FileReader:与FileInputStream对应,主要用来读取字符文件,使用缺省的字符编码,常用的构造函数如表6-9所示。 表6-9 FileReader类构造方法 构 造 方 法 说 明 FileReader(File file) 使用File对象创建文件输入流对象,如果文件打开失败,将抛出异常 FileReader(String name) 使用文件名或路径创建文件输入流对象,如果文件打开失败,将抛出异常 FileWrite:与FileOutputStream对应 ,将字符类型数据写入文件,使用缺省字符编码和缓冲器大小,常用的构造函数如表6-10所示。 表6-10 FileWriter类构造方法 构 造 方 法 说 明 FileWriter(File file) 使用File对象创建文件输出流对象,如果文件打开失败,将抛出异常,必须捕捉 FileWriter(File file, boolean append) 使用File对象创建文件输出流对象,并由参数append指定是否追加,异常情况同上 FileWriter(String name) 直接使用文件名或路径创建文件输出流对象,异常情况同上 FileWriter(String name, boolean append) 直接使用文件名或路径创建文件输出流对象,并由参数append指定是否追加,异常情况同上 下面程序使用FileReader和Writer将“Hello World”写入文件reader.txt,并读取打印出来。 public static void main(String[] args) throws IOException { //把内容写入到文件中 try (FileWriter fw = new FileWriter("reader.txt"); ) { String str = "hello world"; fw.write(str.toCharArray()); } //把内容显示在屏幕上 try(FileReader fr= new FileReader("reader.txt")){ char[] c=new char[20]; int hasRead=0; while((hasRead=fr.read(c))!=-1) { System.out.print(new String(c,0,hasRead)); } } } (3)BufferedReader和BufferedWriter 使用FileRead和FileWriter从文件中逐个地读取/写入字符,效率比较低下,因此一般将该类对象包装到缓冲流(BufferedReader和BufferedWriter)中进行操作。BufferedReader和BufferedWriter与BufferedInputStream和BufferOutputStream操作类似,使用缓冲方式文本读取/写入,初次之外还提供了readLine()和newLine()方法,从而提高了读写的效率。 下面程序使用BufferedReader和BufferedWriter完成文件的写入和读取。 public static void main(String[] args) throws IOException { // 把内容写入到文件中 try (FileWriter fw = new FileWriter("reader.txt"); BufferedWriter bw = new BufferedWriter(fw);) { String str = "hello world"; bw.write(str); bw.newLine(); bw.write("good good study"); } // 把内容显示在屏幕上 try (FileReader fr = new FileReader("reader.txt"); BufferedReader br = new BufferedReader(fr)) { while (br.ready()) { System.out.println(br.readLine()); } } }