Fork me on GitHub
返回顶部
跳到底部

IO

File类

File是java.io包下代表与平台无关的文件和目录,也就是说,如果希望在程序中操作文件和目录,都可以通过File类来完成。不管是文件还是目录都是使用File来操作的,File能新建、删除、重命名文件和目录,File不能访问文件本身。如果需要访问文件本身,需要使用输入输出流。

文件过滤器
File类的list()方法里可以接收一个FilenameFilter参数,通过该参数可以列出只符合条件的文件。FilenameFilter是函数式接口,包含一个accept(File dir,String name)方法。

下面列出当前路径下以.java结尾的文件以及文件夹。

public class FileFilterTest {
    public static void main(String[] args) {
        File file = new File(".");
        String[] nameList=file.list((dir,name)->name.endsWith(".java")
                ||new File(name).isDirectory());
        for (String name : nameList) {
            System.out.println(name);
        }
    }
}

Java把所有设备里的有序数据抽象成流模型,简化了输入/输出处理,Java的IO流的40多个类都是从如下4个抽象积累派生出来的。

  • InputStream/Reader:所有输入流的基类,前者字节输入流,后者字符输入流。
  • OutputStream/Writer:所有输出流的基类,前者是字节输入流,后者是字符输出流。

按流的角色分,可以分为节点流和处理流。
可以从一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,也被称为低级流(low level stream)。
处理流则用于对一个已存在的流连接或封装,通过封装后的流来实现数据读/写功能,处理流也被称为高级流。

处理流的用法

处理流可以隐藏底层设备上节点流的差异,并对外提供方便的输入/输出方法,让程序员只需关心高级流的操作。
使用处理流的典型思路是,使用处理流来包装节点流,程序通过处理流来执行输入/输出功能。
识别处理流的方式很简单,只要流的构造器参数不是一个物理节点,而是一个已经存在的流,那么这种流一定是处理流;而所有的节点流都是直接以物理IO节点作为构造器参数的。

public class PrintStreamTest {
    public static void main(String[] args) {
        try (
            FileOutputStream fos = new FileOutputStream("test.txt");
            PrintStream ps = new PrintStream(fos))
        {
            ps.println("hello world");
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在使用处理流包装了底层节点流之后,关闭输入/输出流资源时,只要关闭最上层的处理流即可。关闭最上层处理流时,系统会自动关闭被该处理流包装的节点流。

转换流

输入/输出流体系中还提供了两个转换流,这两个转换流用于将节点流转换成字符流,其中InputStreamReader将字节流转换成字符输入流,OutputStreamWriter将字节输入流转换成字符输出流。

下面以获取键盘输入为例来介绍转换流。Java使用System.in代表标准输入,即键盘输入,但这个输入流是InputStream类的实例,使用不太方便,而且键盘输入内容都是文本内容,所以可以使用InputStreamReader将其转换成字符输入流,还可以将这个字符输入流再次包装成BufferedReader,利用BufferedReader的readLine()方法可以一次读取一行。

public class KeyinTest {
    public static void main(String[] args) {
        try (InputStreamReader reader = new InputStreamReader(System.in);
            BufferedReader br=new BufferedReader(reader);)
        {
          String line=null;
            while ((line = br.readLine()) != null) {
                if (line.equals("exit")) {
                    System.exit(1);
                }
                System.out.println("输入的内容是:"+line);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

重定向标准输入/输出

Java的标准输入/输出分别通过System.in和System.out来代表,在默认情况下它们分别代表键盘和显示器,当程序通过System.in来获取输入时,实际是从键盘读取输入;当程序试图通过System.out执行输出时,程序总是输出到屏幕上。
在System类里有如下三个重定向标准输入/输出的方法。

  • public static void setIn(InputStream in)
  • public static void setOut(PrintStream out)
  • public static void setErr(PrintStream err)

下面程序通过重定向标准输入流,将System.out的输出重定向到文件输出,而不是屏幕输出。

public class RedirectOut {
    public static void main(String[] args) {
        try (PrintStream ps = new PrintStream("out.txt"))
        {
            System.setOut(ps);
            System.out.println("hello world!");

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

    }
}
public class RedirectIn {
    public static void main(String[] args) {

        try (FileInputStream fis = new FileInputStream("F:\\IDEA_workspace\\javaSE\\src\\main\\java\\top\\sqmax\\chapter15\\RedirectOut.java"))
        {
            System.setIn(fis);
            Scanner sc=new Scanner(System.in);
            sc.useDelimiter("\n");
            while (sc.hasNext()) {
                System.out.println("键盘输入的内容是:"+sc.next());
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

RandomAccessFile

RandomAccessFile是Java输入/输出流体系中功能最丰富的文件访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。而且RandomAccessFile允许只有定位文件定位指针,可以将指针定位到文件的任意位置,进行读写。RandomAccessFile包含如下两个方法来操作文件记录指针。

  • public void seek(long pos)
  • public native long getFilePointer()

下面程序向指定文件追加内容。

public class AppendContent {
    public static void main(String[] args) {
        try (RandomAccessFile raf = new RandomAccessFile("out.txt", "rw")) {
            raf.seek(raf.length());
            raf.write("追加的内容!\r\n".getBytes());
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }
}

RandomAccessFile依然不能想文件指定位置插入内容,如果直接将文件记录指针移动到中间位置后开始输出,则新输出的内容会覆盖文件中原有的内容。我们可以把插入点后面的内容读入缓存区,等把需要插入的数据写入文件后,再将缓存区的内容追加到文件后面。

public class InsertContent {
    public static void insert(String fileName,long pos,String insertContent) {
        File tmp= null;
        try {
            tmp = File.createTempFile("tmp",null);
        } catch (IOException e) {
            e.printStackTrace();
        }
        tmp.deleteOnExit();
        try (
            RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
            RandomAccessFile tempContent = new RandomAccessFile(tmp, "rw")) {
            raf.seek(pos);
            byte[] bbuf = new byte[64];
            int hasRead = 0;
            while ((hasRead = raf.read(bbuf)) > 0) {
                tempContent.write(bbuf, 0, hasRead);
            }
            raf.seek(pos);
            raf.write(insertContent.getBytes());
            while ((hasRead = tempContent.read(bbuf)) > 0) {
                raf.write(bbuf, 0, hasRead);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws Exception{
        insert("InsertContent.java",45,"hello world\r\n");
    }
}
posted @ 2018-05-16 12:39  sqmax  阅读(173)  评论(0编辑  收藏  举报