Thinking in Java——第18章IO系统(一)

Thinking in Java——第18章I/O系统(一)

18.1 File类

  File(文件)类既能代表一个特定的文件的名称,又能代表一个目录下的一组文件的名称。如果指的是一个文件集,可以对此调用list()方法,这个方法返回一个字符数组。

18.1.1 目录列表器

注意:使用Arrays.sort()和String.CASE_INSENSITIVE.ORDERComparator可以快速对一个数组进行按照字母顺序排序。

import java.util.*;
import java.io.*;
import java.util.regex.*;
public class DirList{
    public static void main(String[] args) 
    {
        File path = new File("D:/files/app/src/main/java/com/superdaxue/tingtashuo/fragment");
        String []list;
        if(args.length == 0){
            list = path.list();
        }else{
            list = path.list(new DirFilter(args[0]));
            System.out.println(args[0]);
        }
        Arrays.sort(list,String.CASE_INSENSITIVE_ORDER);
        for (String dirItem : list)
        {
            System.out.println(dirItem);
        }
    }
}
class DirFilter implements FilenameFilter{
    private Pattern pattern;
    public DirFilter(String regex){
        pattern = Pattern.compile(regex);
    }
    public boolean accept(File dir , String name){
        return pattern.matcher(name).matches();
    }
}

18.1.2 目录实用工具

java.io.File.getCanonicalFile()返回此抽象路径名的规范形式

import java.util.regex.*;
import java.util.*;
import java.io.*;
public final class  Directory
{

    public static File[]local(File dir , final String regex){
           return dir.listFiles(new FilenameFilter(){
                  private Pattern pattern = Pattern.compile(regex);
                  public boolean accept(File dir,String name){
                      return pattern.matcher(new File(name).getName()).matches();
                  }
           });
    }

    public static File[]local (String path , final String regex){
        return local(new File(path), regex);
    }

    public static class TreeInfo implements Iterable<File>
    {
        public List<File>files = new ArrayList<File>();
        public List<File>dirs  = new ArrayList<File>();

        public Iterator<File> iterator (){
              return files.iterator();
        }

        void addAll(TreeInfo other){
            files.addAll(other.files);
            dirs.addAll(other.dirs);
        }
        public String toString(){
            return "";//
        }
    }

    public static TreeInfo walk(String start,String regex){
           return recurseDirs(new File(start),regex);
    }

    public static TreeInfo walk(File start , String regex){
        return recurseDirs(start,regex);
    }

    public static TreeInfo walk(File start){
        return recurseDirs(start,".*");
    }

    public static TreeInfo walk(String start){
        return recurseDirs(new File(start),".*");
    }

    static TreeInfo recurseDirs(File startDir , String regex){
           TreeInfo result = new TreeInfo();
           for (File item : startDir.listFiles())
           {
               if (item.isDirectory())
               {
                   result.dirs.add(item);
                   result.addAll(recurseDirs(item,regex));
               }else{
                   if (item.getName().matches(regex))
                   {
                       result.files.add(item);
                   }
               }
           }
           return result;
    }

    public static void main(String[] args) 
    {
//        PPrint.pprint(Directory.walk(PATH).dirs);
//        for (File f : Directory.local(PATH,".*") )
//        {
//          System.out.println(f);
//        }

         new ProcessFiles(new ProcessFiles.Strategy(){
                  public void process(File file){
                       System.out.println(file);
                  }
         },"java").start(args);
    }
}
class PPrint
{
    public static String pformat(Collection<?>c){
        if (c.size() == 0)
        {
            return "[]";
        }
        StringBuilder sb = new StringBuilder("[");
        for ( Object elem : c )
        {
            if(c.size() != 1){
                sb.append("\n");
                sb.append(elem);
            }

        }
        if (c.size() != 1)
        {
            sb.append("\n");
            sb.append("]");         
        }
        return sb.toString();
    }
    public static void pprint(Collection<?>c){
        System.out.println(pformat(c));
    }
    public static void pprint(Object [] o){
        System.out.println(pformat(Arrays.asList(o)));
    }
}
class ProcessFiles
{
    private static final String PATH = "D:/files/app/src/main/java/com/superdaxue/tingtashuo/fragment";

    public interface Strategy
    {
        void process(File file);
    }
    private Strategy strategy;
    private String ext;
    public ProcessFiles(Strategy strategy,String ext){
        this.strategy = strategy;
        this.ext = ext;
    }
    public void start(String [] args){
        try
        {
            if (args.length == 0)
            {
                processDirectoryTree(new File(PATH));
            }else{
                for (String arg : args )
                {
                    File fileArg = new File(arg);
                    if (fileArg.isDirectory())
                    {
                        processDirectoryTree(fileArg);
                    }else{
                        if (!arg.endsWith("."+ext))
                        {
                            arg += "." +ext;                            
                        }
                        strategy.process(new File(arg).getCanonicalFile());
                    }
                }
            }
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
            //e.printStackTrace();
        }

    }
    public void processDirectoryTree (File root) throws IOException{
         for (File file : Directory.walk(root.getAbsolutePath(),".*\\."+ext))
         {
             strategy.process(file.getCanonicalFile());
         }
    }
}

18.1.3 目录的检查及创建

  File类不仅仅只代表存在的文件或目录,也可以用File对象来创建新的目录或尚不存在的整个目录路径。还可以查看文件的特性(如:大小,最后的修改日期,读/写),检查某个File对象代表的是一个文件还是一个目录。

import java.io.*;
class MakeDirectories 
{
    public static void main(String[] args) 
    {
        if (args.length < 1)
        {
            usage();
        }

        if (args[0].equals("-r"))
        {
            if (args.length != 3)
            {
                usage();
            }
            File
                old = new File(args[1]),
                rname = new File(args[2]);
                old.renameTo(rname);
                fileData(old);
                fileData(rname);
                return ;
        }
        int count = 0 ;
        boolean del = false;
        if (args[0].equals("-d"))
        {
            count ++ ;
            del = true;
        }
        count -- ;
        while (++ count < args.length)
        {
            File f = new File(args[count]);
            if (f.exists())
            {
                System.out.println(f + " exists");
                if(del){
                   System.out.println("deleting ... " + f);
                   f.delete();
                }
            }else{
                if (!del)
                {
                    f.mkdirs();
                    System.out.println("created " + f);
                }
                fileData(f);
            }
        }
    }

    private static void usage(){
        System.out.println(
             "Usage:MakeDirectories path1 ... \n"+
             "Creates each path\n" +
             "Usage:MakeDirectories -d path1 ... \n"+
             "Deletes each path\n"+
             "Usage:MakeDirectories -r path1 path2\n"+
             "Renames from path1 to path2"
        );
        System.exit(1);
    }

    private static void fileData(File f){
        System.out.println(
              "Absolute path:"+f.getAbsolutePath() +
              "\n Can read:"+f.canRead() +
              "\n Can write:"+f.canWrite() +
              "\n getName:" +f.getName() +
              "\n length:" +f.getPath() +
              "\n lastModified:"+f.lastModified()
        );
        if(f.isFile()){
            System.out.println("It's a file");
        }else{
            System.out.println("It's a directory");
        }
    }
}

18.2 输入和输出

   Java类库中的I/O类分为输入和输出两部分。任何来自Inputstream或者Reader派生而来的类都含有名为read()的基本用法,用于读取单个字节或者字节数组。同样,任何自OutputStream或Writer派生而来的类名为write()的基本方法,用于写单个字节或者字节数组。

18.2.1 InputStream类型

InputStream:表示从不同数据源产生输入的类。
数据源包括:

  • 字节数组
  • String对象
  • 文件
  • “管道”,工作方式与实际管道类似,即,从一端输入,从另一端输出。
  • 一个由其他种类的流组成的序列,以便我们可以将它们收集合并到一个流内
  • 其他数据源,如Internet连接等
功能构造器参数如何使用
ByteArrayInput允许将内存缓冲区当作InoutStream使用缓冲区,字节从其中取出作为一种数据源:将其与FileterInputStream对象相连以提供有用接口
StringBufferInputStream将String转换成InputStream字符串。底层实现实际使用StringBuffer作为一种数据源:将其与FilterInputStream对象相连提供有用接口
FileInputStream用于从文件中读取信息字符串,表示文件名、文件或FileDescriptor对象作为一种数据源:将其与FileterInputStream对象相连以提供有用接口
PipedInputSream产生用于写入相关PipedOutStream的数据。实现“管道化”PipedIntsream作为多线程中的数据源:将其FilterInputStream对象相连以提供有用接口
SequenceInputStream将两个或多个InputStream对象转换成单一InputStream两个InputStream对象或一个容纳InputStream对象的容器Enumeration作为一种数据源:将其与FilterInputStream对象以提供有用有用接口
FilterInputStream抽象类,作为“装饰器”的接口。其中,“装饰器”为其他的InputStraem提供有用功能
18.2.2 OutputStream类型
功能构造器参数如何使用
ByteArrayOutputStream在内存中创建缓冲区。所有送往”流”的数据都要放置在此缓冲区缓冲区初始化尺寸(可选的)用于指定数据的目的地:将其与FilterOutputStream对象相连以提供接口
FileOutputStream用于将信息写至文件字符串,表示文件名、文件或FileDescriptor对象指定数据的目的地:将其与FilterOutputStream对象相连以提供有用接口
PipedOutputStream任何写入其中的信息都会自动作为相关PipedInputStream的输出。实现“管道化”PipedInputSrteam指定用于多线程的数据的目的地:将其与FilterOutputStream对象相连以提供
FilterOutputStream抽象类,作为“装饰器”的接口。其中,“装饰器”为其他OutputStraem提供有用功能

18.3 添加属性和有用的接口

  FilterInoutStraem 和 FilterOutputStraem是用来提供装饰器接口以控制特定输入流(InputStraem)和输出流(OutputStraem)两个类。FilterInputStram 和 FilterOutputStream分别自I/O类库中的基类InputStream和OutputSrteam派生而来。

18.3.1 通过FilterInputStream从InputStrtream读取数据

  FilterInputStream 类能够完成两件完全不同的事情。DataInputStream允许不同的基本类型数据以及String对象。其他FilterInputStream类则内部修改InputStream的行为方式:是否缓冲,是否保留它所读过的行,以及是否把单一字符推回输入流。

18.4 Reader 和 Writer

InputStreamReader可以把InputStraem转换为Reader,而OutputStreamWriter可以把OutputStream转为Writer

18.4.1数据的来源和出处
18.4.2更改流的行为

回头多看两遍

18.5自我独立的类 :RandomAccessFile

RandomAccessFile适用于由大小已知的记录组成的文件,我们可以使用seek()将记录从一处转移到另一处,然后读取和修改。RandomAccessFile是一个完全独立的类。直接从Object派生。getFilePointer用于查找当前所处的文件位置,seek()用于在文件内移至新的位置,length()用于判断文件的最大尺寸。构造还需要第二个参数用来指示只是”随机读”(r)还是”既读既写”(rw),但不支持只写。

18.6I/O流的典型使用方式

18.6.1缓冲输入文件

想要打开一个文件用于字符输入,可以使用以String或File对象作为文件名的FileInputReader。使用BufferedReader,提供readLine()方法。当readLine()返回null时,就证明达到了文件末尾。

import java.io.*;
class BufferedInputFile 
{
    public static void main(String[] args) throws IOException
    {
         System.out.println(read("BufferedInputFile.java"));
    }
    public static String read(String filename) throws IOException{
           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();
    }
}
18.6.2从内存输入

从BufferedInputFile.read()读入的String结果被用来创建一个StringReader,然后调用read()每次读取一个字符,并把它发送到控制台。

import java.io.*;
class MemoryInput 
{
    public static void main(String[] args) throws IOException
    {
        StringReader in = new StringReader(read("MemoryInput.java"));
        int c ;
        while((c = in.read())!= -1){
              System.out.println((char)c);
        }
    }
    public static String read(String filename) throws IOException{
           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();
    }
}
18.6.3 格式化的内存输入

要读取格式化数据,使用DataInputStream,一个面向字节的I/O类。必须使用InputStream类而不是Reader类。可以用InputStream以字节的形式读取任何数据

import java.io.*;
class  FormattedMemoryInput
{
    public static void main(String[] args) throws IOException
    {
        try
        {
            DataInputStream in = new DataInputStream(new ByteArrayInputStream(
                                  read("FormattedMemoryInput.java").getBytes()  
            ));
            while(true)
                System.out.println((char)in.readByte());
        }
        catch (IOException e)
        {
            System.out.println("End of Stream");
        }
    }

    public static String read(String filename) throws IOException{
           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();
    }
}

这里为ByteArrayInputStream提供字节数组,为了产生该数组String包含了一个可以实现此项工作的getBytes()方法。产生的ByteArrayInputStream是一个适合传递给DataInputStream的InputSream。


如果从DataInputStream用readByte()一次一个字节地读取字符,那么任何字节的值都是合法的结果,这就造成返回值不能用来检测输入是否结束。为了判断是否结束,可以使用available()这个方法查看有多少还有多少可供存取的字符。

import java.io.*;
class TestEOF 
{
    public static void main(String[] args) 
    {
        try
        {
            DataInputStream in = new DataInputStream(
                new BufferedInputStream(new FileInputStream("TestEOF.java"))
            );
            while(in.available() != 0){
                System.out.print((char)in.readByte());
            }
        }
        catch (IOException e)
        {
            System.out.println("End int stream");
        }       
    }
}

**注意**available()的工作方式会随着所读取的的媒介类型的不同而有所不同。字面的意思就是”在没有阻塞的情况下所读取的字节数”。一个文件就是整个文件。这个方法在对于不同的流的时候可能就不是这样,谨慎使用。

18.6.4 基本的文件输出

FileWriter对象可以向文件写入。首先,创建一个与指定文件连接的FileWriter。实际上,通常会用BufferedWriter将其包装起来用以缓冲输出。

import java.io.* ;
class BasicFileOutput 
{
    private static String file = "BasicFileOutput.out";
    public static void main(String[] args) throws IOException
    {
        BufferedReader in = new BufferedReader(new StringReader(read("BasicFileOutput.java")));
        PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(file)));
        int lineCount = 1;
        String s;
        while( (s = in.readLine()) != null){
            out.println(lineCount ++ + ":" +s);
        }
        out.close();
        System.out.println(read(file));
    }

    public static String read(String filename) throws IOException{
           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();
    }
}

一旦读完读完输入流,readLine()会返回null。当读取完时,out调用close()。如果不为所有的输出文件调用close(),就会发现缓冲区内容不会被刷新清空。

18.6.5存储和恢复数据

PrintWriter可以对数据进行格式化。为了输出可供另一”流”恢复的数据,需要用DataOutputStream写入数据,并用DataInputStream恢复数据。注意DataOutputStream和DataInputStream是面向字节的,要使用InputStream和OutStream。


import java.io.*;
class StoringAndRecoveringData 
{
    public static void main(String[] args) throws IOException
    {
        DataOutputStream  out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("Data.txt")));
        out.writeDouble(3.14159);
        out.writeUTF("That was pi");
        out.writeDouble(1.41413);
        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());
    }
}

如果使用DataOutputStream写入数据,Java保证可以使用DataInputStream准确的读取数据——无论读和写数据的平台多么不同。

18.6.6 读写随机访问文件

使用RandomAccessFile时,利用seek()可以在文件中到处移动,并修改文件中的某个值。

import java.io.*;
class UsingRandomAccessFile 
{
    private static String file = "rtest.dat";
    private static void display() throws IOException{
            RandomAccessFile rf = new RandomAccessFile(file,"r");
            for (int i = 0;i < 7 ;i ++ )
            {
                System.out.println(
                       "Value" + i + ":" + rf.readDouble()  
                );
            }
            System.out.println(rf.readUTF());
            rf.close();
    }
    public static void main(String[] args) throws IOException
    {
        RandomAccessFile rf = new RandomAccessFile(file,"rw");
        for (int i = 0 ; i < 7 ; i ++ )
        {
            rf.writeDouble( i * 1.414);
        }
        rf.writeUTF("The end of ths file");
        rf.close();
        display();
        rf = new RandomAccessFile(file,"rw");
        rf.seek(5 * 8);
        rf.writeDouble(47.0001);
        rf.close();
        display();
    }
}

因为double总是8字节长,用seek()查找第5个双精度值,只需用 5 * 8 产生查找位置;
RandomAccessFile除了实现DataInput和DataOutPut接口外,有效地与I/O继承层次结构的其他部分实现了分离。因为它不支持装饰,所以不能将其与InputStream及OutputStream子类的任何部分组合起来。第二个参数可以指定”只读”(r)或”读写”(rw)方式打开一个RandomAccessFile文件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值