第十五章 I/O

I/O

流概述

1.流是一组有序的数据序列,根据操作的类型,可分为输入流和输出流两种。I/O(Input/Output,输入/输出)流提供了一条通道程序,可以使用这条通道把源中的字节序列送到目的地。

输入/输出流

Java语言定义了许多类专门负责各种方式的输入/输出,这些类都被放在java.io包中。其中,所有输入流类都是抽象类InputStream(字节输入流) 或抽象类Reader(字符输入流) 的子类;而所有输出流都是抽象类 OutputStream(字节输出流) 或抽象类Writer(字符输出流) 的子类。

输入流

Inputstream类中所有方法遇到错误时都会引发IOException异常

在这里插入图片描述

Java中的字符是Unicode编码,是双字节的

Reader类具体层次结构:
在这里插入图片描述

输出流

OutputStream类中的所有方法均返回void,在错误时会引发IOException异常

在这里插入图片描述
在这里插入图片描述

File类

File类是java.io包中唯一代表磁盘文件本身的对象。

文件的创建与删除

通常使用一下3种构造方法来创建文件对象:

  1. File(String pathname) :其中,pathname指路径名称(包含文件名)
  2. File(String parent,String child):parent父路径字符串,child 子路径字符串
  3. File(File f,String child):f父路径对象,child 子路径字符串

若:

File file = new File("word.txt");

若当前目录中不存在名为word文件,File类对象可以通过调用createNewFile()方法创建一个名称为word.txt文件;如果存在word.txt文件,可以通过文件对象的delete()方法将其删除。
例:

import java.io.*;

public class FileTest { // 创建类FileTest
	public static void main(String[] args) { // 主方法
		File file = new File("word.txt"); // 创建文件对象
		if (file.exists()) { // 如果该文件存在
			file.delete(); // 将文件删除
			System.out.println("文件已删除"); // 输出的提示信息
		} else { // 如果文件不存在
			try { // try语句块捕捉可能出现的异常
				file.createNewFile(); // 创建该文件
				System.out.println("文件已创建"); // 输出的提示信息
			} catch (Exception e) { // catch处理该异常
				e.printStackTrace(); // 输出异常信息
			}
		}
	}
}

//测试过了,单单创建file对象对文件夹里的文件没有什么用,只有createNewFile()、delete()等方法对文件生效,假如没有delete(),而文件夹里又有word文件,那么没影响。

获取文件信息

在这里插入图片描述

文件输入/输出流

FileInputStream与FileOutputStream类

FileInputStream类常用构造方法:

  • FileInputStream(String name)
  • FileInputStream(File file)

创建一个FileOutputStream对象时,可以指定不存在的文件名,但此文件不能是一个已被其他程序打开的文件。
例:

import java.io.*;

public class FileTest { // 创建类
	public static void main(String[] args) { // 主方法
		File file = new File("word.txt"); // 创建文件对象
		try { // 捕捉异常
			// 创建FileOutputStream对象
			FileOutputStream out = new FileOutputStream(file);
			// 创建byte型数组
			byte buy[] = "我有一只小毛驴,我从来也不骑。".getBytes();
			out.write(buy); // 将数组中信息写入到文件中
			out.close(); // 将流关闭
		} catch (Exception e) { // catch语句处理异常信息
			e.printStackTrace(); // 输出异常信息
		}
		try {
			// 创建FileInputStream类对象
			FileInputStream in = new FileInputStream(file);
			byte byt[] = new byte[1024]; // 创建byte数组
			int len = in.read(byt); // 从文件中读取信息
			// 将文件中信息输出
			System.out.println("文件中的信息是:" + new String(byt, 0, len));
			in.close(); // 关闭流
		} catch (Exception e) {
			e.printStackTrace(); // 输出异常信息
		}
	}
}
Java与其他语言编写的程序进行tcp/ip socket通讯时,通讯内容一般都转换成byte数组型

详情看这:Java中字符和byte数组之间的相互转换.

当使用完流后,显示地关闭所有打开的流仍是一个好习惯。

FileReader和FileWriter类

FileOutputStream类与FileInputStream类只提供了对字节或字节数组的读取方法。由于汉字在文件中占用两个字节,如果使用字节流,读取不好可能会出现乱码现象,此时采用字符流Reader或Writer类即可避免这种现象。
例:这里只copy用到FileReader和FileWrite的部分

public void actionPerformed(ActionEvent e) {
// 创建文件对象
	File file = new File("word.txt");
	try {
			// 创建FileWriter对象
			FileWriter out = new FileWriter(file);
			// 获取文本域中文本
			String s = jTextArea.getText();
			out.write(s); // 将信息写入磁盘文件
			out.close(); // 将流关闭
		} catch (Exception e1) {
			e1.printStackTrace();
		}
}
File file = new File("word.txt"); // 创建文件对象
	try {
			// 创建FileReader对象
			FileReader in = new FileReader(file);
			char byt[] = new char[1024]; // 创建char型数组
			int len = in.read(byt); // 将字节读入数组
			// 设置文本域的显示信息
			jTextArea.setText(new String(byt, 0, len));
			in.close(); // 关闭流
		} catch (Exception e1) {
			e1.printStackTrace();
	}

带缓存的输入/输出流

缓存流为I/O流增加了内存缓存区,有了缓存区,使得在流上执行skip()、mark()和reset()方法都成为可能.

BufferedInputStream与 BufferedOutputStream类

//Buffer即缓存区

BufferedReader与BufferedWriter类

这两个类同样具有内部缓存机制,并可以以行为单位 进行输入/输出
BufferedReader类常用方法:

  • read():读取单个字符
  • readLine():读取一个文本行,并将其返回为字符串。若无数据可读,则返回null
    BufferedWriter:
  • wirter(Strings,int off,int len)方法:写入字符串的某一部分
  • flush()方法:刷新该流的缓存
  • newLine()方法:写入一个分隔符

在使用BufferWriter类的Write()方法时,数据并没有立刻被写入输入流,而是首先进入缓存区中。如果想立刻将缓存区中的数据写入输出流,一定要调用flush()方法。

数据输入/输出流

DataInputStream类与DataOutputStream类,允许应用程序以与机器无关的方式从底层输入流中读取基本Java类型。
DataOutputStream类提供如下三种写入字符串的方法:

  • writeBytes(String s)
  • writeChars(String s)
  • writeUTF(String s)

writeUTF将字符串按照UTF编码后的字节长度写入目标设备,然后才是每一个字节的UTF编码
DataInputStream类只提供了一个**readUTF()**方法返回字符串
DataOutPutStream类中只有writeUTF()方法向目标设备中写入字符串的长度,所以也能准确地读回写入字符串。

ZIP压缩输入/输出流

ZipEntry类产生的对象,是用来代表一个ZIP压缩文件内的进入点(entry)

压缩文件

利用ZipOutStream类对象可将文件压缩为.zip文件。构造方法:

ZipOutputStream(OutputStream out);

ZipOutStream 类常用方法:

方法返回值说明
putNextEntry(ZipEntry e)void开始写入一个新的ZipEntry,并将流内的位置移至此entry所指数据的开头
write(byte[],int off,int len)void将字节数组写入当前ZIP条目数据
finish()void完成写入ZIP输出流的内容,无须关闭它所配合的OutputStream
setComment(String comment)void可设置此ZIP文件的注释文字

例:

import java.io.*;
import java.util.zip.*;

public class MyZip { // 创建类
	private void zip(String zipFileName, File inputFile) throws Exception {
		ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
				zipFileName)); // 创建ZipOutputStream类对象
		zip(out, inputFile, ""); // 调用方法
		System.out.println("压缩中…"); // 输出信息
		out.close(); // 将流关闭
	}
	
	private void zip(ZipOutputStream out, File f, String base)
			throws Exception { // 方法重载
		if (f.isDirectory()) { // 测试此抽象路径名表示的文件是否是一个目录
			File[] fl = f.listFiles(); // 获取路径数组
			out.putNextEntry(new ZipEntry(base + "/")); // 写入此目录的entry
			base = base.length() == 0 ? "" : base + "/"; // 判断参数是否为空
			for (int i = 0; i < fl.length; i++) { // 循环遍历数组中文件
				zip(out, fl[i], base + fl[i]);
			}
		} else {
			out.putNextEntry(new ZipEntry(base)); // 创建新的进入点
			// 创建FileInputStream对象
			FileInputStream in = new FileInputStream(f);
			int b; // 定义int型变量
			System.out.println(base);
			while ((b = in.read()) != -1) { // 如果没有到达流的尾部
				out.write(b); // 将字节写入当前ZIP条目
			}
			in.close(); // 关闭流
		}
	}
	
	public static void main(String[] temp) { // 主方法
		MyZip book = new MyZip(); // 创建本例对象
		try {
			// 调用方法,参数为压缩后文件与要压缩文件
			book.zip("hello.zip", new File("src"));
			System.out.println("压缩完成"); // 输出信息
		} catch (Exception ex) {
			ex.printStackTrace();
		}
	}
}

解压缩ZIP文件

ZipInputStream
常用方法:

方法返回值说明
read(byte[] b,int off,int len)int读取目标b数组内off偏移量的位置,长度是len字节
availableint判断是否已读完目前entry所指定的数据。已读完返回0,否则返回1
closeEntry()void关闭当前ZIP条目并定位流以读取下一个条目
skip(long n)long跳过当前ZIP条目中指定的字节数
getNextEntry()ZipEntry读取下一个ZipEntry,并将流内的位置移至该entry所指数据的开头
createZipEntry(String name)ZipEntry以指定的name 参数新建一个 ZipEntry 对象

实践与练习

  1. 编写程序,实现读取文件时出现一个表示读取进度的进度条。可使用javax.swing包提供的输入流类ProgressMonitorInputStream
    查了下API的上述类:
    public ProgressMonitorInputStream(Component parentComponent,
    Object message,
    InputStream in)构造一个对象,以监视输入流的进度。

参数:
message - 要在对话框(如果弹出)中放置的描述性文本。
parentComponent - 触发被监视操作的组件。
in - 要监视的输入流。

API中的例子:

InputStream in = new BufferedInputStream(
                          new ProgressMonitorInputStream(
                                  parentComponent,
                                  "Reading " + fileName,
                                  new FileInputStream(fileName)));

由例子看出,主要完成parentComponent的设计即可。

import java.io.*;

import javax.swing.*;

public class Student {
	public static void main(String[] temp) {
		byte b[] = new byte[2];
		try{
			FileInputStream fis = new FileInputStream("word.txt");
			ProgressMonitorInputStream in = 
				new ProgressMonitorInputStream(null,"读取文件",fis);
		   while(in.read(b)!=-1){
			   String s = new String(b);
			   System.out.print(s);
			   Thread.sleep(20);
		   }
		}catch (Exception e) {
			e.printStackTrace();
		}
	}
}

上面是答案,想歪了,根本不用设计parentComponent,直接用null都可以- -。然后Thread.sleep这线程很有意思。

  1. 编写程序,使用字符输入、输出流读取文件,将一段文字加密后存入文件,然后再读取,并将加密前与加密后的文件输出。
import java.io.*;

public class Example {
	public static void main(String[] args) {
		char a[] = "今天10点出发".toCharArray();
		int n = 0;
		try {
			File out = new File("word.txt");
			for (int i = 0; i < a.length; i++) {
				a[i] = (char) (a[i] ^ 'R');
			}
			FileWriter fw = new FileWriter(out);
			fw.write(a, 0, a.length);
			fw.close();
			FileReader fr = new FileReader(out);
			char tom[] = new char[10];
			System.out.println("加密后:");
			while ((n = fr.read(tom, 0, 10)) != -1) {
				String s = new String(tom, 0, n);
				System.out.println(s);
			}
			fr.close();
			fr = new FileReader(out);
			System.out.println("明文:");
			while ((n = fr.read(tom, 0, 10)) != -1) {
				for (int j = 0; j < n; j++) {
					tom[j] = (char) (tom[j] ^ 'R');
				}
				String str = new String(tom, 0, n);
				System.out.println(str);
			}
			
			fr.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
	}
	
}
  1. 编程实现当用户输入姓名和密码时,将每一个姓名和密码加在文件中,如果用户输入done,就结束程序
import java.io.*;

public class Example {
	static final int lineLength = 81;
	
	public static void main(String[] args) {
		FileOutputStream fos;
		
		byte[] phone = new byte[lineLength];
		byte[] name = new byte[lineLength];
		try {
			fos = new FileOutputStream("word.txt");
			while (true) {
				System.err.println("请输入一个名字:");
				if ("done".equalsIgnoreCase(new String(name, 0, 0, 4))) {
					System.out.println("录入完毕");
					break;
				}
				System.err.println("请输入电话号:");
				readLine(phone);
				for (int i = 0; phone[i] != 0; i++) {
					fos.write(phone[i]);
				}
				fos.write(',');
				for (int j = 0; name[j] != 0; j++) {
					fos.write(name[j]);
				}
				fos.write('\n');
				System.out.println("信息已经写入文件");
			}
			fos.close();
		} catch (Exception e) {
			// TODO 自动生成 catch 块
			e.printStackTrace();
		}
	}
	
	private static void readLine(byte[] name) throws IOException {
		int b = 0, i = 0;
		while ((i < (lineLength - 1)) && (b = System.in.read()) != '\n') {
			name[i++] = (byte) b;
		}
		name[i] = (byte) 0;
	}
	
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值