压缩
Java提供了对字节流进行压缩和解压缩的类。它们可以在java.util.zip包中被找到,同时也作为Jar文件的基 础(Jar文件是具有一个清单的Zip文件)。
以下的程序采用一个单一的输入文件,并且生成一个压缩了的Zip输出文件,该程序带有一个表示输入文件的 入口项。
import java.io.*;
import java.util.zip.*;
public class compress {
public static void doit(String filein, String fileout) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(filein);
fos = new FileOutputStream(fileout);
ZipOutputStream zos = new ZipOutputStream(fos);
ZipEntry ze = new ZipEntry(filein);
zos.putNextEntry(ze);
final int BUFSIZ = 4096;
byte inbuf[] = new byte[BUFSIZ];
int n;
while ((n = fis.read(inbuf)) != -1)
zos.write(inbuf, 0, n);
fis.close();
fis = null;
zos.close();
fos = null;
}
catch (IOException e) {
System.err.println(e);
}
finally {
try {
if (fis != null)
fis.close();
if (fos != null)
fos.close();
}
catch (IOException e) {
}
}
}
public static void main(String args[]) {
if (args.length != 2) {
System.err.println("missing filenames");
System.exit(1);
}
if (args[0].equals(args[1])) {
System.err.println("filenames are identical");
System.exit(1);
}
doit(args[0], args[1]);
}
}
下一个程序正好相反,采用假定只有一个入口项的Zip输入文件,并且将该项解压到指定的输出文件:
import java.io.*;
import java.util.zip.*;
public class uncompress {
public static void doit(String filein, String fileout) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(filein);
fos = new FileOutputStream(fileout);
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry ze = zis.getNextEntry();
final int BUFSIZ = 4096;
byte inbuf[] = new byte[BUFSIZ];
int n;
while ((n = zis.read(inbuf, 0, BUFSIZ)) != -1)
fos.write(inbuf, 0, n);
zis.close();
fis = null;
fos.close();
fos = null;
}
catch (IOException e) {
System.err.println(e);
}
finally {
try {
if (fis != null)
fis.close();
if (fos != null)
fos.close();
}
catch (IOException e) {
}
}
}
public static void main(String args[]) {
if (args.length != 2) {
System.err.println("missing filenames");
System.exit(1);
}
if (args[0].equals(args[1])) {
System.err.println("filenames are identical");
System.exit(1);
}
doit(args[0], args[1]);
}
}
压缩是有助还是损害I/O性能很大程度上依赖于本地硬件设置;尤其是处理器和磁盘驱动器的相对速度。采用 Zip技术进行压缩,典型地将数据量减少50%,但是有一些压缩和解压的时间开销。在带IDE磁盘驱动器的 300-MHz Pentium PC上,对一个大的压缩文本文件(5-10MB)的实验表明,从磁盘读取压缩文件比非压缩文件 快将近1/3。
充分展现压缩优越性的一个例子是在写入象软盘这样的低速介质。使用高速处理器 (300 MHz Pentium)和低 速软盘(PC上常用的软盘驱动器)进行的测试显示,压缩一个大文本文件,然后写入软盘,比直接将文件拷 贝到软盘快50%。
高速缓存
关于硬件的高速缓存的详细讨论超出了本文的范围。但是,有时软件高速缓存 能够加速I/O。考虑这样一个 例子,需要以随机的方式读取文本文件的某些行。完成的方法之一是读入所有的行,并且把它们存储在 ArrayList(一个与Vector相似的collection类)中:
import java.io.*;
import java.util.ArrayList;
public class LineCache {
private ArrayList list = new ArrayList();
public LineCache(String fn) throws IOException {
FileReader fr = new FileReader(fn);
BufferedReader br = new BufferedReader(fr);
String ln;
while ((ln = br.readLine()) != null)
list.add(ln);
br.close();
}
public String getLine(int n) {
if (n < 0)
throw new IllegalArgumentException();
return (n < list.size() ?
(String)list.get(n) : null);
}
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
LineCache lc = new LineCache(args[0]);
int i = 0;
String ln;
while ((ln = lc.getLine(i++)) != null)
System.out.println(ln);
}
catch (IOException e) {
System.err.println(e);
}
}
}
getLine方法被用来检索任意的一行。这项技术非常有用,但对于大文件而言,显然需要使用了大量的内存, 因此具有局限性。一个替代方法是仅仅记住最近被请求的100行,必要时从磁盘上读取其他行。在存在行的存 储局部性这种情况下,这个方案运行良好,但是需要真正的随机存储时就没有这么好了。
标志化(Tokenization)
标志化(Tokenization)指将字节或者字符序列拆散成象词一样的逻辑块的过程。Java提供了StreamTokenizer 类,可以进行如下的操作:
import java.io.*;
public class token1 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileReader fr = new FileReader(args[0]);
BufferedReader br = new BufferedReader(fr);
StreamTokenizer st = new StreamTokenizer(br);
st.resetSyntax();
st.wordChars('a', 'z');
int tok;
while ((tok = st.nextToken()) !=
StreamTokenizer.TT_EOF) {
if (tok == StreamTokenizer.TT_WORD)
; // st.sval has token
}
br.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}
本例根据小写字母(字母a-z)进行标记。如果由您自己实现了其等效程序,可能看上去象这样:
import java.io.*;
public class token2 {
public static void main(String args[]) {
if (args.length != 1) {
System.err.println("missing filename");
System.exit(1);
}
try {
FileReader fr = new FileReader(args[0]);
BufferedReader br = new BufferedReader(fr);
int maxlen = 256;
int currlen = 0;
char wordbuf[] = new char[maxlen];
int c;
do {
c = br.read();
if (c >= 'a' && c <= 'z') {
if (currlen == maxlen) {
maxlen *= 1.5;
char xbuf[] =
new char[maxlen];
System.arraycopy( wordbuf, 0,
xbuf, 0, currlen);
wordbuf = xbuf;
}
wordbuf[currlen++] = (char)c;
}
else
if (currlen > 0) {
String s = new String(wordbuf, 0,
currlen);
// do something with s
currlen = 0;
}
} while (c != -1);
br.close();
}
catch (IOException e) {
System.err.println(e);
}
}
}