Guava包学习---I/O

Guava的I/O平时使用不太多,目前项目原因导致基本上只有在自己写一些文本处理小工具才用得到。但是I/O始终是程序猿最常遇到的需求和面试必问的知识点之一。同时Guava的I/O主要面向是时JDK5和JDK6的时候的设计,现在已经到了JDK8,然而由于懒惰并没有仔细研究这之间的差别,反正就慢慢看吧。

Guava提供了一个Files的类,用final修饰了,所以直接用就行了,也不要再想着扩展了,里面的方法很多:

获得一个BufferedReader/bufferedwriter,简单就是从file->fileinputstream->inputstreamreader-bufferedreader,一路生成过去:

 public static BufferedReader newReader(File file, Charset charset)
      throws FileNotFoundException {
    checkNotNull(file);
    checkNotNull(charset);
    return new BufferedReader(
        new InputStreamReader(new FileInputStream(file), charset));
  }
  public static BufferedWriter newWriter(File file, Charset charset)
      throws FileNotFoundException {
    checkNotNull(file);
    checkNotNull(charset);
    return new BufferedWriter(
        new OutputStreamWriter(new FileOutputStream(file), charset));
  }

获得一个Guava自定义的ByteSource对象:

public static ByteSource asByteSource(File file) {
    return new FileByteSource(file);
  }

这个FileByteSource其实就是提供了获得文件流、文件大小、获得字节数组、toString()等方法。

获得一个Guava自定义的ByteSink对象:

 public static ByteSink asByteSink(File file, FileWriteMode... modes) {
    return new FileByteSink(file, modes);
  }

ByteSink的概念稍微有点绕,看下源码:

private static final class FileByteSink extends ByteSink {

    private final File file;
    private final ImmutableSet<FileWriteMode> modes;

    private FileByteSink(File file, FileWriteMode... modes) {
      this.file = checkNotNull(file);
      this.modes = ImmutableSet.copyOf(modes);
    }

    @Override
    public FileOutputStream openStream() throws IOException {
      return new FileOutputStream(file, modes.contains(APPEND));
    }

    @Override
    public String toString() {
      return "Files.asByteSink(" + file + ", " + modes + ")";
    }
  }

就是给你一个对象,然后这个对象可以将内容追加到file里面去,它可以提供对该文件的追加操作的输出流。

接下来又是一对输入输出的Char对象:

 public static CharSource asCharSource(File file, Charset charset) {
    return asByteSource(file).asCharSource(charset);
  }
 public static CharSink asCharSink(File file, Charset charset,
      FileWriteMode... modes) {
    return asByteSink(file, modes).asCharSink(charset);
  }

输入对象提供了比如readlines()、concat组合两个sources并返回组合后的内容。

输出对象提供了比如获得writer、writelines等内容。

接下来的几个方法可能是更常用和方便的方法了。

获得字节数组:

  public static byte[] toByteArray(File file) throws IOException {
    return asByteSource(file).read();
  }

获得文件内容的String对象:

  public static String toString(File file, Charset charset) throws IOException {
    return asCharSource(file, charset).read();
  }

为文件写入一个字节数组的内容:

  public static void write(byte[] from, File to) throws IOException {
    asByteSink(to).write(from);
  }

把一个文件内容拷贝到一个输出流中:

  public static void copy(File from, OutputStream to) throws IOException {
    asByteSource(from).copyTo(to);
  }

把一个文件拷贝到另外一个地方:

  public static void copy(File from, File to) throws IOException {
    checkArgument(!from.equals(to),
        "Source %s and destination %s must be different", from, to);----------两个文件必须不同
    asByteSource(from).copyTo(asByteSink(to));
  }

把一个字符序列写入文件:

 public static void write(CharSequence from, File to, Charset charset)
      throws IOException {
    asCharSink(to, charset).write(from);
  }

把一个文件拷贝追加后另外一个文件后面:

 public static void copy(File from, Charset charset, Appendable to)
      throws IOException {
    asCharSource(from, charset).copyTo(to);
  }

判断两个文件是否一致:

 public static boolean equal(File file1, File file2) throws IOException {
    checkNotNull(file1);
    checkNotNull(file2);
    if (file1 == file2 || file1.equals(file2)) {
      return true;
    }

    /*
     * Some operating systems may return zero as the length for files
     * denoting system-dependent entities such as devices or pipes, in
     * which case we must fall back on comparing the bytes directly.
     */
    long len1 = file1.length();
    long len2 = file2.length();
    if (len1 != 0 && len2 != 0 && len1 != len2) {-------------考虑到操作系统级别的不同,仅仅length并不完整
      return false;
    }
    return asByteSource(file1).contentEquals(asByteSource(file2));
  }

创建一个临时目录((-__-)b,这个方法有什么用吗?):

 public static File createTempDir() {
    File baseDir = new File(System.getProperty("java.io.tmpdir"));
    String baseName = System.currentTimeMillis() + "-";

    for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) {
      File tempDir = new File(baseDir, baseName + counter);
      if (tempDir.mkdir()) {
        return tempDir;
      }
    }
    throw new IllegalStateException("Failed to create directory within "
        + TEMP_DIR_ATTEMPTS + " attempts (tried "
        + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')');
  }

创建一个文件(如果不存在)或者修改一下该文件的末次修改时间(如果已经存在),就相当于linux的touch命令:

  public static void touch(File file) throws IOException {
    checkNotNull(file);
    if (!file.createNewFile()
        && !file.setLastModified(System.currentTimeMillis())) {
      throw new IOException("Unable to update modification time of " + file);
    }

为要创建的文件创建路径中出现的其他父文件夹路径然后创建这个File,需要其中某一步执行失败,可以会已经创建了一些父文件夹出来而无法收回:

 public static void createParentDirs(File file) throws IOException {
    checkNotNull(file);
    File parent = file.getCanonicalFile().getParentFile();
    if (parent == null) {--------本身就是根目录
      return;
    }
    parent.mkdirs();
    if (!parent.isDirectory()) {
      throw new IOException("Unable to create parent directories of " + file);
    }
  }

移动文件:

  public static void move(File from, File to) throws IOException {
    checkNotNull(from);
    checkNotNull(to);
    checkArgument(!from.equals(to),
        "Source %s and destination %s must be different", from, to);

    if (!from.renameTo(to)) {
      copy(from, to);
      if (!from.delete()) {
        if (!to.delete()) {
          throw new IOException("Unable to delete " + to);
        }
        throw new IOException("Unable to delete " + from);
      }
    }
  }

只读第一行:

  public static String readFirstLine(File file, Charset charset)
      throws IOException {
    return asCharSource(file, charset).readFirstLine();
  }

用的最多的一个方法,把文件转成一个List<String>:

public static List<String> readLines(File file, Charset charset)
      throws IOException {
    // don't use asCharSource(file, charset).readLines() because that returns
    // an immutable list, which would change the behavior of this method
    return readLines(file, charset, new LineProcessor<List<String>>() {
      final List<String> result = Lists.newArrayList();

      @Override
      public boolean processLine(String line) {
        result.add(line);
        return true;
      }

      @Override
      public List<String> getResult() {
        return result;
      }
    });
  }

返回你定义格式的内容,需要提供一个回掉函数,回掉函数可以返回一个fboolean,如果返回false就结束读取,也就是你可以在这里去自定义自己想要的结束位置:

  public static <T> T readLines(File file, Charset charset,
      LineProcessor<T> callback) throws IOException {
    return asCharSource(file, charset).readLines(callback);
  }
  public static <T> T readBytes(File file, ByteProcessor<T> processor)
      throws IOException {
    return asByteSource(file).read(processor);
  }

返回一个文件的Hash值,你可以自定义hash算法:

  public static HashCode hash(File file, HashFunction hashFunction)
      throws IOException {
    return asByteSource(file).hash(hashFunction);
  }

将文件内容全量映射到内存中,就是JDK1.4以后NIO里面独立于JVM堆之外的直接内存,用于加速IO处理的:

  public static MappedByteBuffer map(File file, MapMode mode)
      throws IOException {
    checkNotNull(file);
    checkNotNull(mode);
    if (!file.exists()) {
      throw new FileNotFoundException(file.toString());
    }
    return map(file, mode, file.length());
  }
  public static MappedByteBuffer map(File file, MapMode mode, long size)
      throws FileNotFoundException, IOException {
    checkNotNull(file);
    checkNotNull(mode);

    Closer closer = Closer.create();
    try {
      RandomAccessFile raf = closer.register(
          new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw"));
      return map(raf, mode, size);
    } catch (Throwable e) {
      throw closer.rethrow(e);
    } finally {
      closer.close();
    }
  }

返回准确的文件路径:

 public static String simplifyPath(String pathname) {
    checkNotNull(pathname);
    if (pathname.length() == 0) {
      return ".";
    }

    // split the path apart
    Iterable<String> components =
        Splitter.on('/').omitEmptyStrings().split(pathname);
    List<String> path = new ArrayList<String>();

    // resolve ., .., and //
    for (String component : components) {
      if (component.equals(".")) {
        continue;
      } else if (component.equals("..")) {
        if (path.size() > 0 && !path.get(path.size() - 1).equals("..")) {
          path.remove(path.size() - 1);
        } else {
          path.add("..");
        }
      } else {
        path.add(component);
      }
    }

    // put it back together
    String result = Joiner.on('/').join(path);
    if (pathname.charAt(0) == '/') {
      result = "/" + result;
    }

    while (result.startsWith("/../")) {
      result = result.substring(3);
    }
    if (result.equals("/..")) {
      result = "/";
    } else if ("".equals(result)) {
      result = ".";
    }

    return result;
  }