常用流的代码测试之字节流

字 节 流

字节流类为处理字节式输入/输出提供了丰富的环境。一个字节流可以和其他任何类型
的对象并用,包括二进制数据。这样的多功能性使得字节流对很多类型的程序都很重要。
因为字节流类以InputStream 和OutputStream为顶层,我们就从讨论这两个类开始。

InputStream(输入流)

InputStream 是一个定义了Java流式字节输入模式的抽象类。该类的所有方法在出错条件下引发一个IOException 异常。

InputStream 定义的方法

方法描述
int available( )返回当前可读的输入字节数
void close( )关闭输入源。关闭之后的读取会产生IOException异常
void mark(int numBytes)在输入流的当前点放置一个标记。该流在读取numBytes个字节前都保持有效
boolean markSupported( )如果调用的流支持mark( )/reset( )就返回true
int read( )如果下一个字节可读则返回一个整型,遇到文件尾时返回-1
int read(byte buffer[ ])试图读取buffer.length个字节到buffer中,并返回实际成功读取的字节数。遇到文件尾时返回-1
int read(byte buffer[ ], int offset,int numBytes)试图读取buffer中从buffer[offset]开始的numBytes个字节,返回实际读取的字节数。遇到文件尾时返回-1
void reset( )重新设置输入指针到先前设置的标志处
long skip(long numBytes)忽略numBytes个输入字节,返回实际忽略的字节数

OutputStream(输出流)

OutputStream是定义了流式字节输出模式的抽象类。该类的所有方法返回一个void 值并且在出错情况下引发一个IOException异常。
OutputStream 定义的方法

方法描述
void close( )关闭输出流。关闭后的写操作会产生IOException异常
void flush( )定制输出状态以使每个缓冲器都被清除, 也就是刷新输出缓冲区
void write(int b)向输出流写入单个字节。注意参数是一个整型数,它允许你不必把参数转换成字节型就可以调用write()
void write(byte buffer[ ])向一个输出流写一个完整的字节数组
void write(byte buffer[ ], int offset,int numBytes)写数组buffer以buffer[offset]为起点的numBytes个字节区域内的内容

FileInputStream(文件输入流)

FileInputStream 类创建一个能从文件读取字节的InputStream 类, 它的两个常用的构造函数如下:

FileInputStream(String filepath)
FileInputStream(File fileObj)

它们都能引发FileNotFoundException异常。这里, filepath 是文件的全称路径, fileObj是描述该文件的File对象。
下面的例子创建了两个使用同样磁盘文件且各含一个上述构造函数的FileInputStreams类:

FileInputStream f0 = new FileInputStream("/autoexec.bat")
File f = new File("/autoexec.bat");
FileInputStream f1 = new FileInputStream(f);

尽管第一个构造函数可能更常用到,
第二个构造函数允许在把文件赋给输入流之前用File方法更进一步检查文件。

当一个FileInputStream被创建时,它可以被公开读取。
FileInputStream重载了抽象类InputStream的六个方法, mark( )和reset( )方法不被重载,任何关于使用FileInputStream的reset()尝试都会生成IOException异常。
下面的例题说明了怎样读取单个字节、字节数组以及字节数组的子界。它同样阐述了
怎样运用available( )判定剩余的字节个数及怎样用skip( )方法跳过不必要的字节。该程序读取它自己的源文件,该源文件必定在当前目录中。

// Demonstrate FileInputStream.
import java.io.*;
class FileInputStreamDemo {
public static void main(String args[]) throws Exception {17 章 输入/输出:探究 java.jo 373
int size;
InputStream f =new FileInputStream("FileInputStreamDemo.java");
System.out.println("Total Available Bytes: " +(size = f.available()));
int n = size/40;
System.out.println("First " + n +" bytes of the file one read() at a time");

for (int i=0; i < n; i++) {
System.out.print((char) f.read());
}
System.out.println("\nStill Available: " + f.available());
System.out.println("Reading the next " + n +" with one read(b[])");

byte b[] = new byte[n];

if (f.read(b) != n) {
System.err.println("couldn't read " + n + " bytes.");
}
System.out.println(new String(b, 0, n));
System.out.println("\nStill Available: " + (size = f.available()));
System.out.println("Skipping half of remaining bytes with skip()");

f.skip(size/2);
System.out.println("Still Available: " + f.available());
System.out.println("Reading " + n/2 + " into the end of array");

if (f.read(b, n/2, n/2) != n/2) {
System.err.println("couldn't read " + n/2 + " bytes.");
}
System.out.println(new String(b, 0, b.length));
System.out.println("\nStill Available: " + f.available());
f.close();
}
}


下面是该程序的输出:
Total Available Bytes: 1433
First 35 bytes of the file one read() at a time
// Demonstrate FileInputStream.
im
Still Available: 1398
Reading the next 35 with one read(b[])
port java.io.*;
class FileInputS
Still Available: 1363
Skipping half of remaining bytes with skip()
Still Available: 682
Reading 17 into the end of array
port java.io.*;
read(b) != n) {
S
Still Available: 665

这个有些刻意创作的例子说明了怎样读取数据的三种方法,怎样跳过输入以及怎样检查流中可以获得数据的数目。

FileOutputStream(文件输出流)

FileOutputStream 创建了一个可以向文件写入字节的类OutputStream,它常用的构造函数如下:

FileOutputStream(String filePath)
FileOutputStream(File fileObj)
FileOutputStream(String filePath, boolean append)

它们可以引发IOException或SecurityException异常。这里filePath是文件的全称路径,
fileObj是描述该文件的File对象。如果append为true,文件以设置搜索路径模式打开。
FileOutputStream的创建不依赖于文件是否存在。在创建对象时FileOutputStream在打开输出文件之前创建它。这种情况下你试图打开一个只读文件,会引发一个IOException异常。

下面的例子创建一个样本字节缓冲器。先生成一个String对象,接着用getBytes( )方法取字节数组对等体。然后创建了三个文件。第一个file1.txt将包括样本中的各个字节。第二个文件是file2.txt,它包括所有字节。第三个也是最后一个文件file3.txt,仅包含最后的四
分之一。

不像FileInputStream类的方法,所有FileOutputStream类的方法都返回一个void类型值。在出错情况下,这些方法将引发IOException异常。

// Demonstrate FileOutputStream.
import java.io.*;
class FileOutputStreamDemo {
public static void main(String args[]) throws Exception {
String source = "Now is the time for all good men\n"
+ " to come to the aid of their country\n"
+ " and pay their due taxes.";
byte buf[] = source.getBytes();
OutputStream f0 = new FileOutputStream("file1.txt");
for (int i=0; i < buf.length; i += 2) {
f0.write(buf[i]);
}
f0.close();
OutputStream f1 = new FileOutputStream("file2.txt");
f1.write(buf);
f1.close();
OutputStream f2 = new FileOutputStream("file3.txt");
f2.write(buf,buf.length-buf.length/4,buf.length/4);
f2.close();
}
}

下面是运行该程序之后,每个文件的内容,
首先是file1.txt:
Nwi h iefralgo e
t oet h i ftercuty n a hi u ae.

接着,是file2.txt:
Now is the time for all good men
to come to the aid of their country
and pay their due taxes.

最后, file3.txt
nd pay their due taxes.

ByteArrayInputStream(字节数组输入流)

ByteArrayInputStream是把字节数组当成源的输入流。该类有两个构造函数,每个构造函数需要一个字节数组提供数据源:

ByteArrayInputStream(byte array[ ])
ByteArrayInputStream(byte array[ ], int start, int numBytes)

这里, array是输入源。第二个构造函数创建了一个InputStream类,该类从字节数组的子集生成,以start指定索引的字符为起点,长度由numBytes决定。
下面的例子创建了两个ByteArrayInputStream,用字母表的字节表示初始化它们:

// Demonstrate ByteArrayInputStream.
import java.io.*;
class ByteArrayInputStreamDemo {
public static void main(String args[]) throws IOException {
String tmp = "abcdefghijklmnopqrstuvwxyz";
byte b[] = tmp.getBytes();
ByteArrayInputStream input1 = new ByteArrayInputStream(b);
ByteArrayInputStream input2 = new ByteArrayInputStream(b, 0,3);
}
}

input1对象包含整个字母表中小写字母, input2仅包含开始的三个字母。
ByteArrayInputStream实现mark( )和reset( )方法。然而,如果 mark( )不被调用, reset( )在流的开始设置流指针——该指针是传递给构造函数的字节数组的首地址。

下面的例子说明了怎样用reset( )方法两次读取同样的输入。这种情况下,我们读取数据,然后分别用小
写和大写字母打印“abc”。

import java.io.*;
class ByteArrayInputStreamReset {
public static void main(String args[]) throws IOException {
String tmp = "abc";
byte b[] = tmp.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(b);
for (int i=0; i<2; i++) {
int c;
while ((c = in.read()) != -1) {
if (i == 0) {3762 部分 Java 库
System.out.print((char) c);
} else {
System.out.print(Character.toUpperCase((char) c));
}
}
System.out.println();
in.reset();
}
}
}

该例先从流中读取每个字符,然后以小写字母形式打印。然后重新设置流并从头读起,
这次在打印之前先将字母转换成大写字母。下面是输出:
abc
ABC

ByteArrayOutputStream(字节数组输出流)

ByteArrayOutputStream是一个把字节数组当作输出流的实现。 ByteArrayOutputStream
有两个构造函数,如下:

ByteArrayOutputStream( )
ByteArrayOutputStream(int numBytes)

在第一种形式里,一个32位字节的缓冲器被生成。第二个构造函数生成一个跟指定numBytes相同位数的缓冲器。缓冲器保存在ByteArrayOutputStream的受保护的buf 成员里。

缓 冲 器 的 大 小 在 需 要 的 情 况 下 会 自 动 增 加 。

缓 冲 器 保 存 的 字 节 数 是 由ByteArrayOutputStream的受保护的count域保存的。

下面的例子说明了ByteArrayOutputStream:
// Demonstrate ByteArrayOutputStream.

import java.io.*;
class ByteArrayOutputStreamDemo {
public static void main(String args[]) throws IOException {
ByteArrayOutputStream f = new ByteArrayOutputStream();
String s = "This should end up in the array";
byte buf[] = s.getBytes();
f.write(buf);
System.out.println("Buffer as a string");
System.out.println(f.toString());
System.out.println("Into array");
byte b[] = f.toByteArray();
for (int i=0; i<b.length; i++) {
System.out.print((char) b[i]);
}
System.out.println("\nTo an OutputStream()");
OutputStream f2 = new FileOutputStream("test.txt");
f.writeTo(f2);
f2.close();
System.out.println("Doing a reset");
f.reset();
for (int i=0; i<3; i++)
f.write('X');
System.out.println(f.toString());
}
}

运行程序后,生成下面的输出。注意在调用reset( )之后,三个X怎样结束。
Buffer as a string
This should end up in the array
Into array
This should end up in the array
To an OutputStream()
Doing a reset
XXX

该例用 writeTo( )这一便捷的方法将f 的内容写入test.txt,检查在前面例子中生成的test.txt文件内容,结果如下:
This should end up in the array

过滤字节流

过滤流(filtered stream)仅仅是底层透明地提供扩展功能的输入流(输出流)的包装。
这些流一般由普通类的方法(即过滤流的一个超类)访问。典型的扩展是缓冲,字符转换和原始数据转换。这些过滤字节流是FilterInputStream 和FilterOutputStream。它们的构造函
数如下:

FilterOutputStream(OutputStream os)
FilterInputStream(InputStream is)

这些类提供的方法和InputStream 及OutputStream类的方法相同。

缓冲字节流

对于字节流,缓冲流(buffered stream),通过把内存缓冲器连到输入/输出流扩展一个过滤流类。该缓冲器允许Java对多个字节同时进行输入/输出操作,提高了程序性能。
因为缓冲器可用,所以可以跳过、标记和重新设置流。缓冲字节流类是BufferedInputStream 和BufferedOutputStream。

PushbackInputStream 也可实现缓冲流。
BufferedInputStream(缓冲输入流)
缓冲输入/输出是一个非常普通的性能优化。

Java 的BufferedInputStream 类允许把任何
InputStream 类“包装”成缓冲流并使它的性能提高。
BufferedInputStream 有两个构造函数:

BufferedInputStream(InputStream inputStream)
BufferedInputStream(InputStream inputStream, int bufSize)

第一种形式生成了一个默认缓冲长度的缓冲流。

第二种形式缓冲器大小是由bufSize传入的。使用内存页或磁盘块等的若干倍的缓冲区大小可以给执行性能带来很大的正面影响。
但这是依赖于执行情况的。最理想的缓冲长度一般与主机操作系统、可用内存空间及机器
配置有关。

合理利用缓冲不需要特别复杂的操作。一般缓冲大小为8192个字节,给输入/输出流设定一个更小的缓冲器通常是好的方法。用这样的方法,低级系统可以从磁盘或网络
读取数据块并在缓冲器中存储结果。因此,即使你在InputStream外同时读取字节数据时,也可以在超过99.9%的时间里获得快速存储操作。

缓冲一个输入流同样提供了在可用缓冲器的流内支持向后移动的必备基础。除了在任何InputStream类中执行的read( )和skip( )方法外, BufferedInputStream 同样支持mark( ) 和
reset( )方法。 BufferedInputStream.markSupported( )返回true是这一支持的体现。
下面的例子设计了一种情形,该情形下,我们可以使用mark( )来记忆我们在输入流中的位置,然后用reset( )方法返回该位置。这个例子分析了HTML实体的引用为版权信息的情
况。这个引用以一个&符号开始以分号(;)结束,没有任何空格。例子输入由两个& 符号来说明何处reset( )发生,何处不发生的情况。

// Use buffered input.
import java.io.*;
class BufferedInputStreamDemo {
public static void main(String args[]) throws IOException {
String s = "This is a &copy; copyright symbol " +
"but this is &copy not.\n";
byte buf[] = s.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(buf);
BufferedInputStream f = new BufferedInputStream(in);
int c;
boolean marked = false;
while ((c = f.read()) != -1) {
switch(c) {
case '&':
if (!marked) {
f.mark(32);
marked = true;
} else {
marked = false;
}
break;
case ';':
if (marked) {
marked = false;
System.out.print("(c)");
} else
System.out.print((char) c);
break;
case ' ':
if (marked) {
marked = false;
f.reset();17 章 输入/输出:探究 java.jo 379
System.out.print("&");
} else
System.out.print((char) c);
break;
default:
if (!marked)
System.out.print((char) c);
break;
}}
}}

注意该例运用mark(32),该方法保存接下来所读取的32个字节(这个数量对所有的实体引用都足够)。

下面是程序的输出:
This is a © copyright symbol but this is &copy not.

警告: 在缓冲器中使用 mark( )是受限的。意思是说你只能给mark( )定义一个小于流缓冲大小的参数。

BufferedOutputStream(缓冲输出流)
BufferedOutputStream与任何一个OutputStream相同,除了用一个另外的flush( ) 方法来保证数据缓冲器被写入到实际的输出设备。因为BufferedOutputStream是通过减小系统写数据的时间而提高性能的,可以调用flush( )方法生成缓冲器中待写的数据。

不像缓冲输入,缓冲输出不提供额外的功能, Java中输出缓冲器是为了提高性能的。
下面是两个可用的构造函数:

BufferedOutputStream(OutputStream outputStream)
BufferedOutputStream(OutputStream outputStream, int bufSize)

第一种形式创建了一个使用512字节缓冲器的缓冲流。第二种形式,缓冲器的大小由bufSize参数传入。
PushbackInputStream(推回输入流)
缓冲的一个新颖的用法是实现推回(pushback)。 Pushback用于输入流允许字节被读取然后返回(即“推回”)到流。

PushbackInputStream类实现了这个想法。它提供了一种机
制来“窥视”在没有受到破坏的情况下输入流生成了什么。
PushbackInputStream有两个构造函数:

PushbackInputStream(InputStream inputStream)
PushbackInputStream(InputStream inputStream, int numBytes)

第一种形式创建了一个允许一个字节推回到输入流的流对象。
第二种形式创建了一个

具有numBytes长度缓冲区的推回缓冲流。它允许多个字节推回到输入流。
除了具有与InputStream相同的方法, PushbackInputStream提供了unread( )方法,表示如
下:

void unread(int ch)
void unread(byte buffer[ ])
void unread(byte buffer, int offset, int numChars)

第一种形式推回ch的低位字节,它将是随后调用read( )方法所返回的下一个字节。
第二种形式返回buffer缓冲器中的字节。

第三种形式推回buffer中从offset处开始的numChars个字
节。如果在推回缓冲器为满时试图返回一个字节, IOException异常将被引发。
Java 2 对PushbackInputStream作了一些小的修改:它实现skip( )方法。
下面的例子演示一个编程语言解析器怎样用PushbackInputStream和unread( )来处理 == 操作符和=操作符之间的不同的。

// Demonstrate unread().
import java.io.*;
class PushbackInputStreamDemo {
public static void main(String args[]) throws IOException {
String s = "if (a == 4) a = 0;\n";
byte buf[] = s.getBytes();
ByteArrayInputStream in = new ByteArrayInputStream(buf);
PushbackInputStream f = new PushbackInputStream(in);
int c;
while ((c = f.read()) != -1) {
switch(c) {
case '=':
if ((c = f.read()) == '=')
System.out.print(".eq.");
else {
System.out.print("<-");
f.unread(c);
}
break;
default:
System.out.print((char) c);
break;
}}}}

下面是例子程序的输出。注意==被“.eq”代替而=被“<-”代替。

if (a .eq. 4) a <- 0;

注意:PushbackInputStream具有使InputStream生成的 mark( ) 或 reset( )方法失效的副作用。

用markSupported( )来检查你运用mark( )/reset( )的任何流类。

SequenceInputStream(顺序输入流)

SequenceInputStream类允许连接多个InputStream流。 SequenceInputStream的构造不同于任何其他的InputStream。 SequenceInputStream构造函数要么使用一对InputStream,要么用
InputStream的一个Enumeration,显示如下:
S

equenceInputStream(InputStream first, InputStream second)
SequenceInputStream(Enumeration streamEnum)

操作上来说,该类满足读取完第一个InputStream后转去读取第二个流的读取要求。
使用Enumeration的情况下,它将继续读取所有InputStream流直到最后一个被读完。
下面是用SequenceInputStream输出两个文件内容的例子程序:

// Demonstrate sequenced input.
import java.io.*;
import java.util.*;
class InputStreamEnumerator implements Enumeration {
private Enumeration files;
public InputStreamEnumerator(Vector files) {
this.files = files.elements();
}
public boolean hasMoreElements() {
return files.hasMoreElements();
}
public Object nextElement() {
try {
return new FileInputStream(files.nextElement().toString());
} catch (Exception e) {
return null;
}
}
}
class SequenceInputStreamDemo {
public static void main(String args[]) throws Exception {
int c;
Vector files = new Vector();
files.addElement("/autoexec.bat");
files.addElement("/config.sys");
InputStreamEnumerator e = new InputStreamEnumerator(files);
InputStream input = new SequenceInputStream(e);
while ((c = input.read()) != -1) {
System.out.print((char) c);
}
input.close();
}
}

该 例 创 建 了 一 个 Vector向 量 并 向 它 添 加 了 两 个 文 件 名 。 它 把 名 字 向 量 传 给
InputStreamEnumerator类,设计该类是为了提供向量包装器,向量返回的元素不是文件名,而是用这些名称打开FileInputStream流。 SequenceInputStream依次打开每个文件,该程序打
印了两个文件的内容。

PrintStream(打印流)

PrintStream具有本书开始以来我们在System文件句柄使用过的System.out所有的格式
化性能。 PrintStream有两个构造函数:

PrintStream(OutputStream outputStream)
PrintStream(OutputStream outputStream, boolean flushOnNewline)

当 flushOnNewline控制 Java每次刷新输出流时,输出一个换行符(\n)。如果flushOnNewline为true,自动刷新。若为false,刷新不能自动进行。第一个构造函数不支持自动刷新。Java的PrintStream对象支持包括Object在内的各种类型的 print( ) 和 println( )方法。如果参数不是一个简单类型, PrintStream方法将调用对象的 toString( ) 方法,然后打印结果。

RandomAccessFile(随机访问文件类)

RandomAccessFile 包 装 了 一 个 随 机 访 问 的 文 件 。 它 不 是 派 生 于 InputStream 和OutputStream,而是实现定义了基本输入/输出方法的DataInput和DataOutput接口。它同样支持定位请求——也就是说,可以在文件内部放置文件指针。它有两个构造函数:

RandomAccessFile(File fileObj, String access)
throws FileNotFoundException
RandomAccessFile(String filename, String access)
throws FileNotFoundException

第一种形式, fileObj指定了作为File 对象打开的文件的名称。
第二种形式,文件名是由filename参数传入的。两种情况下, access 都决定允许访问何种文件类型。如果是“r”,那么文件可读不可写,如果是“rw”,文件以读写模式打开。下面所示的seek( )方法,用来设置文件内部文件指针的当前位置:

void seek(long newPos) throws IOException

这里, newPos 指文件指针从文件开始以字节方式指定新位置。调用seek( )方法后,接下来的读或写操作将在文件的新位置发生。
RandomAccessFile 实现了用来读写随机访问文件的标准的输入和输出方法。下面是Java 2增添的新方法setLength( )。它有下面的形式:

void setLength(long len) throws IOException

该方法通过指定的len设置正在调用的文件的长度。该方法可以增长或缩短一个文件。如果文件被加长,增加的部分是未定义的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欢乐大象

对自己负责,不断提高加油啊废材

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值