Java I/O


1 File class

不要被它的名字所欺骗,它不只代表文件,确切的说应该是FilePath。


1.1 A directory lister(FilenameFilter的使用)

import java.util.regex.Pattern;
import java.io.FilenameFilter;
import java.io.File;

public class DirList {
    public static void main(String[] args) {
	File path = new File(args[0]);
	for (File item : path.listFiles(new DirFilter(args[1]))) {
	    System.out.println(item.getName() + "->" +
			       item.getAbsolutePath());
	}
    }
    static class DirFilter implements FilenameFilter {
	private Pattern p;
	public DirFilter(String regex) {
	    p = Pattern.compile(regex);
	}
	// It must be implemented.
	public boolean accept(File dir, String name) {
	    System.out.println("  dir: " + dir.getName() +
			       "  abs: " + dir.getAbsolutePath());
	    System.out.println("  name: " + name); // name 为当前目录下文件名字,不包含路径
	    return p.matcher(name).matches();
	}
    }
}
测试结果如下图:




FilenameFilter 接口用来过滤你想要的文件。更好的做法是用匿名内部类实现,即:

new file.listFiles(new FilenameFilter() {
    //....
    boolean accept(File dir, String name) {
        //...
    }
});

2 Input and Output

Java I/O 库分为输入和输出,任何来源于InputStream 或Reader的类都有基本方法read():读取单个byte或一个array;任何来源于OutputStream 或Writer的类都有基本方法write():写入单个byte或一个array;

然而,通常我们并不直接使用他们,他们的存在是为了让其他类可以使用他们。


根据功能分类是有益的。在Java 1.0中,设计者们决定,任何与input相关的都继承于InputStream,任何与输出相关的都继承于OutputStream.


2.1 InputStream 的类型

InputStream的工作是代表一个产生一个输入的类,这些输入来自于不同的数据源,这些数据源包括:

(1)An array of bytes: ByteArrayInputStream

(2)A String object     :  StringBufferInputStream(现在已过时)

(3)A file                    :  FileInputStream

(4)A "pipe"               :  PipedInputStream

(5)A sequence of other streams     : SequenceInputStream     

上面这些的每一个都有相应的子类(冒号后面的)。

除了这些外,FilterInputStream也属于InputStream,他提供一个base class for “decorator”,用以附属一些属性或有用的接口到相应的输入流当中。


2.2 OutputStream的类型

与InputStream类似:

(1)An array of bytes: ByteArrayOutputStream

(2)A String object     :  StringBufferOutputStream

(3)A file                    :  FileOutputStream

(4)A "pipe"               :  PipedOutputStream

(5)A sequence of other streams     : SequenceOutputStream     


3 Adding attributes  and useful interfaces 

提供decorator接口以控制特定的InputStream或OutputStream的类是FilterlnputStream andFilterOutputStream(可惜没有一个看了就让人知道的名字)。他们本身也是InputStream和OutputStream。


3.1 Reading from an InputStream with FilterlnputStream

FilterlnputStream 完成两个重要的事。

(1)DataInputStream读取原始类型,如int, float以及String等。

(2)剩下的FilterlnputStream改变lnputStream的内部行为:

        如:是否buffered、unbuffered?是否记录正在读取的行号?等等

任何时候你几乎都需要buffer你的输入。

下面这个表是它的一些类型:



3.2 Writing to an OutputStream with FilterOutputStream

与上面类似。

提一下PrintStream,它的两个重要的方法是print()和println(),是重载的以打印不同的类型。

FilterOutputStream的一些类型如下:





4 Readers 和 Writers


Java 1.1对核心的Java IO 库做了重大改变。当你看到Reader和Writer的时候,你首先想到的可能是他们是来替换Stream的,其实不是的!

尽管一些原始的stream library已经过时了,但InputStream 和 OutputStream 仍然以面向字节(byte-oriented)的形式提供有价值的功能!ReaderWriter提供面向(Unicode)字符(char-oriented)的.


几乎所有的原始Java I/O stream 类都有相应的Reader和Writer类以提供Unicode字符操作。如下图:



4.1 改变stream的行为

对于InputStream和OutputStream来说,streams用相应的FilterInputStream和FilterOutputStream 的“decorator”子类适配于特定需求。Reader和Writer继续使用这个观念——but not exactly.

For InputStreams and OutputStreams, streams were adapted for particular needs using "decorator" subclasses of FilterInputStream and FilterOutputStream. The Reader and Writer class hierarchies continue the use of this idea—but not exactly.

如:BufferedInputStream, BufferedOutputStream类的构造函数分别包含一个InputStream和OutputStream参数;

      BufferedReader, BufferedWriter类的构造函数中分别包含一个Reader和Writer参数。


下面这个表中是与上面两个表类似的。不同之处是类的组织;尽管BufferedOutputStream是FilterOutputStream的子类,但BufferedWriter却不是FilterWriter的子类。






5 I/O streams的一些典型应用


5.1  Buffered input file

打开一个文件用以字符输入,你使用FileReader, 它需要一个String或File作为文件的名字。为了加速,你想使文件to be buffered,因此你给BufferedReader的构造函数一个FileReader的resulting reference。

import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;

public class BufferedInputFile {
    public static String read(String filename) throws IOException {
	// BufferedReader: a reader filter
	// FileReaer     : decorator use
	BufferedReader in = new BufferedReader(new FileReader(filename));
	String s;
	StringBuilder sb = new StringBuilder();
	while ((s = in.readLine()) != null) {
	    sb.append(s + "\n");
	}
	in.close();
	return sb.toString();
    }
    public static void main(String[] args) throws IOException {
	System.out.print(read("BufferedInputFile.java"));
    }
}


The FileReader is decorated as a BufferedReader.
结果:



5.2 Basic file output

import java.io.BufferedReader;
import java.io.StringReader;
import java.io.PrintWriter;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.File;

public class BasicFileOut {
    static String file = "BasicFileOut.out";
    public static void main(String[] args) throws IOException {
	long start = System.currentTimeMillis();
	BufferedReader in= new BufferedReader(
           new StringReader(BufferedInputFile.read("BasicFileOut.java")));
	PrintWriter out = new PrintWriter(
	   new BufferedWriter(new FileWriter(file)));
	//PrintWriter out = new PrintWriter(new File(file));
	int lineCount = 1;
	String s;
	while ((s = in.readLine()) != null) {
	    out.println(lineCount++ + ": " + s);
	}
	out.close();
	System.out.println(BufferedInputFile.read(file));
	long end = System.currentTimeMillis();
	System.out.println("time: " + (end - start));
    }
}

BufferedWriter is decorated as PrintWriter.


Java SE5 added a helper constructor to PrintWriter so that you don’t have to do all the decoration by hand every time you want to create a text file and write to it. Here’s BasicFileOutput.java rewritten to use this shortcut:

import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.IOException;

public class FileOutputShortcut {
    public static String file = "FileOutputShortcut.out";
    public static void main(String[] args)  throws IOException {
	BufferedReader br = new BufferedReader(
	    new StringReader(
	        BufferedInputFile.read("FileOutputShortcut.java")));
	// Here is the shortcut
	PrintWriter pw = new PrintWriter(file);
	int lineCount = 1;
	String s;
	while ((s = br.readLine()) != null) {
	    pw.println(lineCount++ + ": " + s);
	}
	pw.close();
    }
}


6 标准I/O: (1)standard input   (2)standard output   (3)standard error

先看一下System类的一些东西:

属性:



注意:System.in是原始的InputStream,而System.err和System.out是已经包装好了的PrintStream。这意味着你可以直接使用System.err和System.out(如System.out.println()就是直接使用的例子),但使用System.in之前必须进行包装(must be wrapped)。


6.1 Reading from standard input

下面是从标准输入读取数据:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

public class Echo {
    public static void main(String[] args) throws IOException {
	// System.in must be wrapped before used
	BufferedReader stdin = new BufferedReader(
	    new InputStreamReader(System.in));
	String s;
	while ((s = stdin.readLine()) != null && s.length() != 0) {
	    System.out.println(s);
	}
    }
}


6.2 Changing System.out to a PrintWriter


System.out 是一个PrintStream, 而PrintStream 又是一个OutputStream. PrintWriter 有一个构造函数,它取OutputStream作为一个参数。因此,如果你愿意,你可以转化System.out成一个PrintWriter:

import java.io.PrintWriter;

public class ChangeSystemOut {
    public static void main(String[] args) {
	PrintWriter out = new PrintWriter(System.out, true);
	out.println("Hello World!");
    }
}

6.3 Redirecting standard I/O 重定向标准I/O

Java的System类允许你重定向标准输入,标准输出,以及标准错误输出。使用静态方法调用:

setIn(InputStream)
setOut(PrintStream)
setErr(PrintStream)

import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.PrintStream;
import java.io.IOException;
import java.io.FileOutputStream;

public class Redirecting {
    public static void main(String[] args) throws IOException {
	PrintStream console = System.out;
	BufferedInputStream bis = new BufferedInputStream(
	  new FileInputStream("Redirecting.java"));
	PrintStream out = new PrintStream(
	  new BufferedOutputStream(new FileOutputStream("test.out")));
	System.setIn(bis);
	System.setOut(out);
	System.setErr(out);
	BufferedReader b= new BufferedReader(
	     new InputStreamReader(System.in));
	String s;
	while ((s = b.readLine()) != null) {
	    System.out.println(s);
	}
	out.close();
	System.setOut(console);
    }
}


7 New I/O

从JDK 1.4引进,在java.nio.*包中,它只有一个目的:加速。实际上,以前的I/O包已经用nio重新实现了!因此,尽管你不直接使用nio,你也会从中获益。

加速来源于使用接近操作系统方式的结构执行I/O:channels和buffers

好比一个煤矿:channel是含有煤(数据)的矿,buffer是你送它到矿(channel)的车子。车子满载煤(数据),你从车子(buffer)中得到煤(数据)。也就是说你不用直接与channel交互,你与buffer交互。channel从buffer中获取数据或放入数据。


唯一一个直接与channel交互的是ByteBuffer,它存储 raw bytes。

老的I/O的三个类已改变用以产生一个FileChannelFileInputStream, FileOutputStream,RandomAccessFile.

注意到这些是字节操作流,与底层的nio一致。

Reader和Writer这两个字符模式的类不产生channels, 但 java.nio.channels.Channels 类提供了从channels中产生Reader和Writer的方法。


下面是一个简单例子,练习三种流以产生channels:(1)writeable, (2)read/writeable, and(3)readable


import java.nio.channels.FileChannel;
import java.nio.ByteBuffer;
import java.io.FileInputStream;
import java.io.RandomAccessFile;
import java.io.FileOutputStream;

public class GetChannel {
    private static final int BSIZE = 1024;
    public static void main(String[] args) throws Exception {
	//Write a file
	FileChannel fc = new FileOutputStream("data.txt").getChannel(); 
	fc.write(ByteBuffer.wrap("Some text ".getBytes()));
	fc.close();
	//Read and write a file
	fc = new RandomAccessFile("data.txt", "rw").getChannel();
	fc.position(fc.size());
	fc.write(ByteBuffer.wrap("Some more ".getBytes()));
	fc.close();
	//Read a File
	fc = new FileInputStream("data.txt").getChannel();
	ByteBuffer buff = ByteBuffer.allocate(BSIZE);
	fc.read(buff);
	buff.flip();
	while (buff.hasRemaining()) {
	    System.out.print((char)buff.get());
	}
	fc.close();
    }
}


7.1 View buffers

下面是一个很好的例子(越来越觉得Thinking in Java很值得重复研究了):

import java.nio.DoubleBuffer;
import java.nio.ShortBuffer;
import java.nio.IntBuffer;
import java.nio.FloatBuffer;
import java.nio.CharBuffer;
import java.nio.ByteBuffer;
import java.nio.LongBuffer;

public class ViewBuffers {
    public static void main(String[] args) {
	ByteBuffer bb = ByteBuffer.wrap(
		       new byte[]{ 0, 0, 0, 0, 0, 0, 0, 'a' });
	bb.rewind();
	System.out.print("Byte Buffer ");
	while(bb.hasRemaining())
	    System.out.print(bb.position() + " -> " + bb.get() + ", ");
	System.out.println();
	CharBuffer cb =
	    ((ByteBuffer)bb.rewind()).asCharBuffer();
	System.out.print("Char Buffer ");
	while(cb.hasRemaining())
	    System.out.print(cb.position() + " -> " + cb.get() + ", ");
	System.out.println();
	FloatBuffer fb =
	    ((ByteBuffer)bb.rewind()).asFloatBuffer();
	System.out.print("Float Buffer ");
	while(fb.hasRemaining())
	    System.out.print(fb.position() + " -> " + fb.get() + ", ");
	System.out.println();
	IntBuffer ib =
	    ((ByteBuffer)bb.rewind()).asIntBuffer();
	System.out.print("Int Buffer ");
	while(ib.hasRemaining())
	    System.out.print(ib.position() + " -> " + ib.get() + ", ");
	System.out.println();
	LongBuffer lb =
	    ((ByteBuffer)bb.rewind()).asLongBuffer();
	System.out.print("Long Buffer ");
	while(lb.hasRemaining())
	    System.out.print(lb.position() + " -> " + lb.get() + ", ");
	System.out.println();
	ShortBuffer sb =
	    ((ByteBuffer)bb.rewind()).asShortBuffer();
	System.out.print("Short Buffer ");
	while(sb.hasRemaining())
	    System.out.print(sb.position() + " -> " + sb.get() + ", ");
	System.out.println();
	DoubleBuffer db =
	    ((ByteBuffer)bb.rewind()).asDoubleBuffer();
	System.out.print("Double Buffer ");
	while(db.hasRemaining())
	    System.out.print(db.position() + " -> " + db.get() + ", ");
    }
}


输出如下:





7.2 下面看一下Buffer details

A Buffer 由数据和四个索引(mark, position, limit, capacity)组成用以有效的访问和操作这些数据。


外加一个

public final Buffer rewind()
 
 
Rewinds this buffer. The position is set to zero and the mark is discarded.

Invoke this method before a sequence of channel-write or get operations, assuming that the limit has already been set appropriately. For example:

 out.write(buf);    // Write remaining data
 buf.rewind();      // Rewind buffer
 buf.get(array);    // Copy data into array
Returns:
This buffer


下面这个例子是交换buffer中相邻的两个字符:

import java.nio.CharBuffer;
import java.nio.ByteBuffer;

public class UsingBuffers {
    private static void symmetricScramble(CharBuffer buffer) {
	while (buffer.hasRemaining()) {
	    buffer.mark();
	    char c1 = buffer.get();
	    char c2 = buffer.get();
	    buffer.reset();
	    buffer.put(c2).put(c1);
	}
    }
    public static void main(String[] args) {
	char[] data = "UsingBuffers".toCharArray();
	ByteBuffer bb = ByteBuffer.allocate(data.length * 2);
	CharBuffer cb = bb.asCharBuffer();
	cb.put(data);
	System.out.println(cb.rewind());
	symmetricScramble(cb);
	System.out.println(cb.rewind());
	symmetricScramble(cb);
	System.out.println(cb.rewind());
    }
}














  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值