JavaIO操作(1)字节流和字符流-2

3.2.2、字节输入流:InputStream

如果现在要从指定的数据源之中读取数据,使用InputStream,而这个类的定义如下:

public abstract class InputStream

extends Object

implements Closeable

在InputStream类之中,定义了三个读取数据的操作方法:

· 读取单个字节:public abstract int read() throws IOException;

|- 说明:每次执行read()方法都会读取一个数据源的指定数据,如果现在发现已经读取到了结尾返回-1;

· 读取多个字节:public int read(byte[] b) throws IOException;

|- 说明:如果现在要读取的数据小于byte的数据,这个时候read()方法的返回值int返回的是数据个数,如果现在开辟的字节数组小于读取的长度,如果数据已经读完了,则这个时候的int返回的是-1;

· 读取指定多个字节:public int read(byte[] b, int off, int len) throws IOException。

 

既然InputStream为抽象类,那么这个抽象类要使用就必须有子类,现在是通过文件读取内容,肯定使用FileInputStream子类进行操作,与OutputStream类的使用一样,对于FileInputStream也只关心构造方法:

· FileInputStream类构造方法:public FileInputStream(File file) throws FileNotFoundException;

范例:一次性全部读取数据进来

package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

public class TestDemo {

public static void main(String[] args) throws Exception {

File file = new File("D:" + File.separator + "hellodemo"

+ File.separator + "test.txt"); // 定义文件路径

if (file.exists()) { // 文件存在则可以读取

InputStream input = new FileInputStream(file) ;

byte data[] = new byte[1024]; // 假设要读的长度是1024

int len = input.read(data) ; // 读取数据,返回读取个数

input.close() ; // 关闭

System.out.println("读取的数据是:【" + new String(data, 0, len) + "】");

}

}

}

在使用手机的过程之中短信功能就是这种操作,每一个短信只能发送70个字,不管是否够70都按70算,因为接收短信的时候是开辟了一个指定长度的空间进行接收。

范例:单个字节读取

package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

public class TestDemo {

public static void main(String[] args) throws Exception {

File file = new File("D:" + File.separator + "hellodemo"

+ File.separator + "test.txt"); // 定义文件路径

if (file.exists()) { // 文件存在则可以读取

InputStream input = new FileInputStream(file);

byte data[] = new byte[1024]; // 假设要读的长度是1024

int foot = 0; // 操作data数组的脚标

int temp = 0;

do {

temp = input.read(); // 读取了一个字节

if (temp != -1) {

data[foot++] = (byte) temp; // 保存读取进来的单个字节

}

} while (temp != -1);// 没有读取完,还有内容

input.close(); // 关闭

System.out.println("读取的数据是:【" + new String(data, 0, foot) + "】");

}

}

}

很明显,对于不确定个数只知道结束条件的循环,只能采用while或者是do..while,而在讲解这两种循环的时候也强调过:几乎不会去使用do…while,主要使用while循环,那么以上的代码也可以修改为while循环。

范例:使用while循环修改之前的程序

package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.InputStream;

public class TestDemo {

public static void main(String[] args) throws Exception {

File file = new File("D:" + File.separator + "hellodemo"

+ File.separator + "test.txt"); // 定义文件路径

if (file.exists()) { // 文件存在则可以读取

InputStream input = new FileInputStream(file);

byte data[] = new byte[1024]; // 假设要读的长度是1024

int foot = 0; // 操作data数组的脚标

int temp = 0;

// 第一步:temp = input.read(),读取一个单个字节,并且将内容给temp变量

// 第二步:temp != -1,将接收到的temp的数值判断是否为-1,如果为-1则表示退出循环,如果不是,则保存数据

while ((temp = input.read()) != -1) {

data[foot++] = (byte) temp; // 保存读取进来的单个字节

}

input.close(); // 关闭

System.out.println("读取的数据是:【" + new String(data, 0, foot) + "】");

}

}

}

在日后的所有开发之中,都会使用以上的while循环方式进行数据的读取。

3.2.3、字符输出流:Writer

Writer类也是一个专门用于数据输出的操作类,这个类定义:

public abstract class Writer

extends Object

implements Appendable, Closeable, Flushable

在Wirter类之中定义的write()方法都是以字符数据为主,但是在这些方法之中,只关心一个:

· 输出一个字符串:public void write(String str) throws IOException。

在Wirter类之中比OutputStream类最为方便的一点就是其可以直接使用String型数据输出,并且不再需要将其变为字节数组了。而Writer类本身也是一个抽象类,那么如果要使用依然要依靠它的子类,尤其是现在操作的是文件,使用FileWriter子类。

范例:使用Wirter类进行内容的输出

package cn.mldn.demo;

import java.io.File;

import java.io.FileWriter;

import java.io.Writer;

public class TestDemo {

public static void main(String[] args) throws Exception {

File file = new File("D:" + File.separator + "hellodemo"

+ File.separator + "test.txt"); // 定义文件路径

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs();// 创建父目录

}

Writer out = new FileWriter(file);

String data = "Hello World .";

out.write(data) ; // 直接输出字符串

out.close() ; // 关闭输出

}

}

从输出来讲,Wirter类的输出要比OutputStream类更加的方便。

3.2.4、字符输入流:Reader

Reader是进行字符数据读取的操作类,其定义:

public abstract class Reader

extends Object

implements Readable, Closeable

在Writer类之中存在了直接输出一个字符串数据的方法,可是在Reader类之中并没有定义这样的方法,只是定义了三个按照字符读取的方法,为什么会这样?

因为在使用OutputStream输出数据的时候,其程序可以输出的大小一定是程序可以承受的数据大小,那么如果说使用InputStream读取的时候,可能被读取的数据非常的大,那么如果一次性全读进来了,就会出现问题,所以只能一个一个的进行读取。

Reader依然是抽象类,那么如果从文件读取,依然使用FileReader类。

package cn.mldn.demo;

import java.io.File;

import java.io.FileReader;

import java.io.Reader;

public class TestDemo {

public static void main(String[] args) throws Exception {

File file = new File("D:" + File.separator + "hellodemo"

+ File.separator + "test.txt"); // 定义文件路径

if (file.exists()) {

Reader in = new FileReader(file); // 字符输入流

char data[] = new char[1024]; // 开辟数组

int len = in.read(data); // 读取数据

System.out.println("读取数据内容:【" + new String(data, 0, len) + "】");

in.close() ;

}

}}

字符比字节的好处就是在于字符串数据的支持上,而这个好处还只是在Writer类中体现,所以与字节流相比,字符流的操作并不是对等的关系。

3.2.5、字节流和字符流的区别

通过以上的代码演示,现在可以发现,对于字节流和字符流可以完成类似的功能,那么在开发之中使用那一种呢?

两者的区别:

字节流在进行IO操作的时候,直接针对的是操作的数据终端(例如:文件),而字符流操作的时候不是直接针对于终端,而是针对于缓存区(理解为内存)操作,而后由缓存取操作终端(例如:文件),属于间接操作,按照这样的方式,如果说在使用字节流的时候不关闭最后的输出流操作,也可以将所有的内容进行输出,而字符输出流的时候如果不关闭,则意味着缓冲区之中的内容不会被输出,当然,这个时候可以由用户自己去调用flush()方法进行强制性的手工清空:

package cn.mldn.demo;

import java.io.File;

import java.io.FileWriter;

import java.io.Writer;

public class TestDemo {

public static void main(String[] args) throws Exception {

File file = new File("D:" + File.separator + "hellodemo"

+ File.separator + "test.txt"); // 定义文件路径

if (!file.getParentFile().exists()) {

file.getParentFile().mkdirs();// 创建父目录

}

Writer out = new FileWriter(file);

String data = "Hello World .";

out.write(data) ; // 直接输出字符串

out.flush() ; // 清空缓冲区

}

}

对于电脑磁盘或者是网络数据传输上,使用最多的数据类型都是字节数据,包括图片、音乐、各种可执行程序也都是字节数据,很明显,字节数据要比字符数据更加的广泛,但是在进行中文处理的过程之中,字符流又要比字节流方便许多,所以如果要使用的话,首先考虑的是字节流(还有一个原因是因为字节流和字符流的代码形式类似),如果有中文处理的问题,才会考虑使用字符流。

主要的区别:

· 字节流没有使用到缓冲区,而字符流使用了;

· 处理各种数据都可以通过字节流完成,而在处理中文的时候使用字符流会更好。

3.2.6、思考题(核心)

要求模拟dos系统的文件拷贝命令,由初始化参数输入源文件和拷贝文件的路径,而后执行文件拷贝操作。

提示:本程序暂时不考虑类的设计,所有的代码都在主方法中完成。考虑复制一个大文件的情况,例如:500M以上。

实现思路:

如果要想实现文件的复制那么肯定要使用的是字节流,因为文件有可能是图片,而对于这样的操作有两种实现方式; · 方式一:将要复制的文件内容全部读取到内存之中,而后将所有内容一次输出到目标文件之中;

· 方式二:采用边读边写的方式一点一点的进行文件的拷贝。

很明显,使用方式二会更加的方便,因为如果要复制的文件很大,那么方式一是不可能在内存之中装如此只大的数据量,所以全部读取是不太可能的。

范例:代码的基础实现

package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.OutputStream;

public class Copy {

public static void main(String[] args) throws Exception {

if (args.length != 2) { // 长度不是2,命令有错误

System.out.println("执行程序命令错误!");

System.exit(1); // 程序退出

}

File inFile = new File(args[0]); // 要复制的源文件

if (!inFile.exists()) { // 源文件不存在

System.out.println("源文件不存在!");

System.exit(1); // 程序退出

}

File outFile = new File(args[1]); // 目标文件

InputStream input = new FileInputStream(inFile);

OutputStream output = new FileOutputStream(outFile);

int temp = 0;

while ((temp = input.read()) != -1) { // 有内容

output.write(temp); // 输出一个内容

}

input.close() ;

output.close() ;

}

}

但是这种代码有一个问题:以上的代码适合于很小的文件,但是文件稍微大一些了,那么肯定不适合。因为这种做法是采用一个个字节复制的方式完成的,根本就不适合于实际的开发,如果要想开发的更加实际一些,应该采用一块一块的读取和输出方式完成。

【核心】范例:改进的做法

package cn.mldn.demo;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.OutputStream;

public class Copy {

public static void main(String[] args) throws Exception {

if (args.length != 2) { // 长度不是2,命令有错误

System.out.println("执行程序命令错误!");

System.exit(1); // 程序退出

}

long start = System.currentTimeMillis() ;

File inFile = new File(args[0]); // 要复制的源文件

if (!inFile.exists()) { // 源文件不存在

System.out.println("源文件不存在!");

System.exit(1); // 程序退出

}

File outFile = new File(args[1]); // 目标文件

InputStream input = new FileInputStream(inFile);

OutputStream output = new FileOutputStream(outFile);

int temp = 0;

byte data [] = new byte[1024 * 1024] ; // 每次读取1024

while ((temp = input.read(data)) != -1) { // 有内容

output.write(data, 0, temp); // 输出一个内容

}

long end = System.currentTimeMillis() ;

System.out.println("拷贝完成,所花费的时间:" + (end - start));

input.close() ;

output.close() ;

}

}

在日后的开发之中,如果给出的是一个InputStream,要求你通过这个InputStream读取出来的数据保存到指定的文件之中的时候,就采用以上代码,这些操作在日后学习Struts框架的时候会出现。

posted @ 2013-04-10 13:10  谷文仁  阅读(258)  评论(0编辑  收藏  举报