File类
{Args:"D.*\.java"}
public class DirList{
public static void main(String [] args){
File path =new File(".");
String [] list;
if(args.length==0)
list=path.list();
else
list=path.list(new DirFilter(arg[0]));
Arrays.sort(list,String.CASE_INSENTIVE_ORDER);
for(String dirItem :list)
System.out.println(dirItem);
}
}
class DirFileter implements FilenameFilter{
private Pattern pattern;
public DirFilter(String regex){
pattern=Pattern.complex(regex);
}
public boolean accept(File dir,String name){
return pattern.matcher(name).matcher();
}
}/*output:
DirectoryDemo.java
DirList.java
DirList2.java
DirList3.java
*/
这里,DirFileter类实现了FilenameFilter接口。
publi interface FilenameFilter{
boolean accept(File dir,String name);
}
DirFilter这个类存在的唯一原因就是将accept()方法,提供给list()使用,使list()可以回调accept(),进而决定哪些文件包含在列表中。
这种结构常常称为回调。更具体地说,这是一个策略模式的例子,因为list()实现里基本的功能,而且按照FilenameFilter的形式提供策略。
下面是策略设计模式的另一个设计:
public class ProcessFiles{
public interface Strategy{
void process(File file);
}
private Strategy strategy;
private String ext;
public ProcessFiles(Strategy strategy,Strign ext){
this.strategy=strategy;
this.ext=ext;
}
public void start(String [] args){
try{
if(args.length==0)
processDirectoryTree(fileArg);
else{
for(String arg :args){
File fileArg=new File(arg);
if(fileArg.idDirectory())
processDirectoryTree(fileArg);
else{
if(!arg.endsWith("."+ext))
arg+="."+ext;
strategy.process(new File(arg).getCannonicalFile());
}
}
}catch(IOException e){
throw new RuntimeException(e);
}
}
}
public void processDirectoryTree(File root) throw IOException{
for(File file: Directory.walk(root.getAbsolutePath(),".*\\."+ext))
strategy.process(file.getCannonicalFile());
}
public static void main(String[] args){
new ProcessFiles(new ProcessFiles.Strategy(){
public void process(File file){
System.out.println(file);
}
},"java").start(args);
}
}
Strategy接口内嵌ProcessFiles中,使得如果你希望实现它,就必须实现ProcessFiles.Strategy。ProcessFiles执行了查找具有特定扩展名的文件所需的全部工作,并且当它找到匹配的文件时,将直接把文件传递给Strategy对象。如果你没有提供任何参数,那么ProcessFiles就假设你希望遍历当前目录下的所有目录。
输入和输出
通过继承,任何自Inputstream或Reader派生而来的类都含有名为read()的基本方法,用于读取单个字节或者字节数组。同样,任何自OutputStream或Writer派生而来的类都含有名为write()的基本方法,用于写单个字节或字节数组。
InputStream类型(所有和输入有关的都是它的子类)
InputStream的作用是用来表示那些从不同数据源产生的输入的类。
- 字节数组
- String对象
- 文件
- “管道”,工作方式和实际管道相似,即从一段输入,从另一端输出。
一个有其他种类的流组成的序列,比便我们可以将他们收集并到一个流内。
每一种数据源都有相应的InputStream子类.另外,FilterInputStream也属于一种InputStream,为”装饰器”类提供基类,其中”装饰器”类可以把属性或有用的接口与输入流连接在一起。
表1-1 InputStream
类 | 功能 | 构筑器参数,即使用 |
---|---|---|
ByteArrayInputStream | 允许将内存的缓冲区当中InputStream的使用 | 缓冲区,字节将从中取出。 作为一种数据源:将其与FilterInputStream对象相连以提供有用的接口 |
StringBufferInputStream | 将String转换成InputStream | 字符串,底层实现实际使用StringBuffer。 作为一种数据源:将其与FilterInputStream对象相连以提供有用接口 |
FileInputStream | 用于从文件中读取信息 | 字符串,表示文件名、文件或FilterInputStream对象相连以提供有用的借口 |
PipedInputStream | 产生用于写入相关PipedOutputStream的数据。实现”管道化”的概念 | PipedOutputStream.作为多线程中数据源:将其与FilterInputStream对象相连以提供有用接口 |
SequenceInputStream | 将两个或多个InputStream对象转换成单一InputStream | 两个InputStram对象或一个容纳InputStream对象的容器Enumeration.作为一种数据源:将其与FilterInputStream对象相连以提供有用的接口。 |
FilterInputStream | 抽象类,作为”装饰器”的接口。其中,“装饰器”为其他的Inputtream类提供有用的功能.见表1-3 | 见表1-3 见表1-3 |
OutputStream类型
**如表1-2所示,该类别的类决定了输出所要去往的目标:字节数组(但不是String,不过你当然可以用字节数组自己创建)、文件或管道。
另外,FilterOutputStream为"装饰器"类提供了一个基类,其中"装饰器"类可以把属性或有用的接口与输入流连接在一起。**
**表1-2 OutputStream**
类 | 功能 | 构筑器参数,即使用 |
---|---|---|
ByteArrayOutputStream | 在内存中创建缓冲区。所有送往”流”的数据都要放置在此缓冲区 | 缓冲区初始化尺寸(可选的)。 用于指定数据的目的地:将其与FilterOutputStream对象相连以提供有用的接口 |
FileOutputStream | 用于将信息写至文件 | 字符串,表示文件名、文件或FileDescriptor对象。 指定数据的目的地:将其与FilterOutputStream对象相连以提供有用接口。 |
PipedOutputStream | 任何写入其中的信息都会自动作为相关PipedInputStream的输出。实现”管道化”概念。 | PipedInputStream指定用于多线程的数据的目的地:将其与FilterOutputStream对象相连以提供有用借口。 |
FilterOutPutStream | 抽象类,作为”装饰器”的接口。其中,”装饰器”为其他OutputStream提供有用功能。见表1-4 | 见表1-4;见表1-4 |
添加属性和有用的接口
FilterInputStream和FilterOutputStream是用来提供装饰器类接口以控制特定输入流(InputStream)和输出流(OutputStream)的两个类。也是他们的子类。
通过FilterInputStream从InputStream读取数据
FilterInputStream类能够完成两件完全不同的事情。其中,DataInputStream允许我们读取,不同的基本类型数据以及String对象(所有方法都以”read”开头,例如readByte(),readFloat()等)。搭配相应的DataOutputStream,我们就可以通过数据”流”将基本类型的数据从一个地方迁徙至另一个地方。
表1-3 FilterInputStream类型
类 | 功能 | 构筑器参数,即使用 |
---|---|---|
DataInputStream | 与DataOutputStream搭配使用,因此我们可以按照可移植方式从流读取基本数据类型(int,char,long等) | InputStream包括用于读取基本类型的全部接口 |
BufferedInputStream | 使用它可以防止每次读取时都得进行实际写操作。代表”使用缓冲区” | InputStream,可以指定缓冲区大小。 本质上不是提供接口,只不过是想进程中添加缓冲区所必需的。与接口对象搭配 |
LineNumberInputStream | 跟踪输入流中的行号;可调用getLineNumber()和setLineNumber(int) | InputStream 。仅增加了行号,因此可能要与接口对象搭配使用 |
PushbackInputStream | 具有”能弹出一个字节的缓冲区”。因此可以将读到的最后一个字符回退 | InputStream。通常作为编辑器的扫描器,之所以包含在内是因为java编译器的需要,我们可能永远不会用到 |
通过FilterOutputStream向OutputStream写入
表1-4 FilterOutputStream类型
类 | 功能 | 构筑器参数,即使用 |
---|---|---|
DataOutputStream | 与DataInputStream搭配使用,因此可以按照可移植方式向流中写入基本类型数据(int,char,long等) | OutPutStream包含用于写入基本类型数据全部接口 |
PrintStream | 用于产生格式化输出。其中DataOutputStream处理数据的存储,PrintStream处理显示 | OutputStream,可以用boolean值指示是否在每次换行时清空缓存区应该是对OutputStream对象的”final”封装.可能会常常使用它。 |
BufferedOutputStream | 使用它以避免每次发送数据时都要进行实际的写操作。代表”使用缓冲区”.可以调用flush()清空缓存区 | OutputStream,可以指定缓冲区大小。 本质上并不是提供接口,只不过是向进程中添加缓冲区所必需的。与接口对象搭配 |
Reader和Writer
Reader和writer提供兼容Unicode与面向字符的I/O功能。有时候我没必须把来自”字节”层次结构中的泪和”字符”层次结构中的类结合起来使用。为了实现这个目的,要用到”适配器”adapter类:InputStreamReader可以把InputStream转换成Reader,而OutputStreamWriter可以把OutputStream转换成Writer.
1.数据的来源和去处
2.更改流的行为
有一点很清楚:无论我们何时使用readLine(),都不应该使用DataInputStream(这会遭到编译器的强烈反对),而应该使用BufferedReader.出来这一点,DataInputStream仍是I/O类库的首选成员。
为了更容易地过渡到使用PrintWriter,它提供了一个既能接受Writer对象又能接受任何OutputStream对象的构造器。PrintWriter的格式化接口实际上与PrintStream相同。
3 I/O流的典型使用方式
3.1 缓冲输入文件
public class BufferedInputFile{
public static String read(String filename) throws IOException{
BufferedReader in=new BufferedReader(new FileReader(filename);
String s;
StringBuilder sb=new BufferedReader();
while((s=in.readLine()!=null)
sb.append(s+"\n");
in.close();
return sb.toString();
}
public static void main(String [] args)
throws IOException{ System.out.println(read("BufferedInputFile.java"));
}
}
字符串sb用来积累文件的全部内容(包括必须添加的换行符,因为readLine()已将它们删除)。最后,调用close()方法关闭文件。
3.2 从内存输入
下面实例,用到了上述3.1的BufferedInputFlie.java代码。
public class MemoryInput{
public static void main(String[] args)
throws IOException{
StringReader in=new StringReader(BufferredInputFile.read("MemoryInput.java"));
int c;
while((c=in.read())!=-1)
System.out.println((char)c);
}
}
注意read()是以int 形式返回下一字节,因此必须类型转换为char才能正确打印。
3.3 格式化的内存输入
下面实例,用到了上述3.1的BufferedInputFlie.java代码。
要读取格式化数据,可以使用DataInputStream,它是以面向字节的I/O类。因此我们必须使用InputStream类而不是Reader类。
public class FormattedMemoryInput{
public static void main(String [] args)
throws IOException{
try{
DataInputStream in=new DataInputStream(new ByteArrayInputStream(BufferedInputFile.read("FormattedMemotyInput.java").getBytes()));
while(true)//or while(in.available()!=0)
System.out.print((char)in.readByte());
}catch(EOFException e){
System.out.prinln("End of stream");
}
}
}
必须为ByteArrayInputStream提供字节数组。ByteArrayInputStream是一个适合传递给DataInputStream的InputStream.
注意,available()的工作方式会随着所读取得媒介类型的不同而不同。字面上就是“在没阻塞的情况下所能读取的字节数”.对于文件,这意味着整个文件;对于不同类型的刘,可能就不是这样。
3.4 基本的文件输出
下面实例,用到了上述3.1的BufferedInputFlie.java代码。
FileWriter对象可以向文件写入数据。首先,创建一个与指定文件连接的FileWriter.实际上,我们通常会用BufferedWriter将其包装起来以缓冲输出。本例中,为了提供格式化机制,它被装饰成了PrintWriter.按照这种方式创建的数据文件可作为普通文本文件的读取。
public class BasicFileOutput{
static String file="BasicFileOutput.out";
public static void main(String [] args)
throws IOException{
BufferedReader in=new BufferedReader(new StringReader(BufferedInputFile.read("BasicFileoutput.java")));
PrintWriter out =new PrintWriter(new BufferedWriter(new FileWriter(file)));
//or PrintWriter out=new PrintWriter(file);
int lineCount=1;
String s;
while((s=in.readLine())!=null)
out.println(lineCount++ +":"+s);
int lineCount=1;
String s;
while((s=in.readLine())!=null)
out.println(lineCount++ +":"+s);
out.close();
System.out.println(BufferedInputFile(file));
}
}
一旦读完输入数据流,readLine()会返回Null.只有out显示调用close(),缓冲区的内容才会被刷新。
也可以使用or后面的快捷方式。你仍然是在进行缓存只是不必自己去实现。遗憾的事其他常见的写入任务,都没用快捷方式。
3.5 存储和恢复数据
PrintWriter可以对数据进行格式化,以便人们的阅读。但是为了输入可供另一个”流”恢复的数据,我们需要用DataOutputStream写数据,并用DataInputStream恢复数据。当然流可以是任何形式.
public class StoringAndRecoveringData{
public static void main(String [] args)
throws IOException{
DataOutputStream out=new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));
out.writeDouble(3.13159);
out.writeUTF("Square root of 2");
out.close();
DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream("Data.txt")));
System.out.println(in.readDouble());
System.out.println(in.readUTF());
System.out.println(in.readDouble());
System.out.println(in.readUTF());
}
}/*output:
3.13159
That was pi
1.41413
Square root of 2
*/
如果我们使用DataOutputStream写入数据,java保证我们可以使用DataInputStream准确地读取数据–无论读和写数据的平台多么不同.
但是为了保证所有的读方法都能正常工作,我们必须知道流中数据项所在的确切位置,因为极有可能将保持的double数据作为一个简单的字节序列、char或其他类型读入。因此我们必须:要么为文件中的数据采用固定的格式;要么将额外的消息保存在文件中,比便能够对其进行解析。注意,对象序列化和xml可能是更容易存储和读取复杂数据结构的方式。
3.6 读取随机
使用RandomAccessFile,类似于组合了DataInputStream和DataOutPutStream(因为它实现了相同的接口:DataInput和DataOutput).另外我们可以看到,利用seek()可以在文件中移动,并修改某个值。
public class UsingRandomAccessFile{
static String file="rtest.dat";
stativ void display() throws IOException{
RandomAccessFile rf=new RandomAccessFile(file,"r");
for(int i=0;i<7;i++)
System.out.println(rf.readUTF());
rf.close();
}
public static void main(String [] args)
Throws IOException{
RandomAccessFile rf=new RandomAccessFile(file,"rw");
for(i=0;i<7;i++)
rf.writeDouble(i*1.414);
rf.writeUTF("The end of the file");
rf.close();
display();
rf=new RandomAccessFie(file,"rw");
rf.seek(5*8);
rf.writeDouble(47.0001);
rf.close();
display();
}/*Output
value 0:0.0
value 1:1.414
value 2:2.828
value 3:4.242
value 4:5.656
value 5:7.069999999999999
value 6:8.484
The end of the file
value 0:0.0
value 1:1.414
value 2:2.828
value 3:4.242
value 4:5.656
value 5:47.0001
value 6:8.484
The end of the file
*/
RandomAccessFile不支持装饰,第二个参数可以指定”r(只读)”或”rw(读写)”.
3.7.1 文件读写的一个实用工具
public class TextFile extends ArrayList<String>{
public static String read(String fileName){
StringBuilder sb=new StringBuilder();
try{
BufferedReader in=new BufferedReader(
new FileReader(new File(fileName)
.getAbsoluteFile()));
try{
String s;
while((s=in.readLine())!=null){
sb.append(s);
sb.append("\n");
}
}finally{
in.close();
}
}catch(IOException e){
throw new RuntimeException(e);
}
return sb.toString();
}
public static void write(String fileName,String text){
try{
PrintWriter out=new PrintWriter(
new File(fileName).getAbsoluteFile());
try{
out.print(text);
}finally{
out.close();
}
}catch(IOException e){
throw new RuntimeException(e);
}
}
public TextFile(String fileName,String splitter){
super(Arrays.asList(read(fileName).split(splitter)));
if(get(0).equals("")) remove(0);
}
public TextFile(String fileName){
this(fileName,"\n");
}
public void write(String fileName){
try{
PrintWriter out=new PrintWriter(
new File(fileName).getAbsoluteFile());
try{
for(String item:this)
out.println(item);
}finally{
out.close();
}
}catch(IOException e){
throw new RuntimeException(e);
}
}
public static void main(Stirng []args){
String file=read("TextFile.java");
write("test.txt",file);
TextFile text=new TextFile("test.txt");
text.write("test2.txt");
TreeSet<String> words=new TreeSet<String>(
new TextFile("TextFile.java","\\W+"));
System.out.println(words.headSet("a"));
}
}/*Output:
[0,ArrayList,Arrays,Break,BufferedReader,BufferedWriter,Clean,Display,File,FileReader,FileWriter,IOException,Normally,Output,PrintWriter,Read,Regulay,RuntimeException,Simple,Static,String,StringBuilder,System,TextFile,Tools,TreeSet,W,Write]
*/
3.7.2 读取二进制文件
public class BinaryFile{
public static byte[] read(File bFile) Throws
IOException{
BufferredInputStream bf=new BufferedInputStream(new FileInputStream(bFile));
try{
byte[] data=new byte[bf.available()];
bf.read(data);
return data;
}finally{
bf.close();
}
}
public static byte[] read(String bFile)
throws IOException{
return read(new File(bFile).getAbsoluteFile());
}