`
cucaracha
  • 浏览: 141774 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
博客专栏
A8f3fa2f-18ce-3862-897a-5d2209197c90
Java NIO.2
浏览量:88110
7a076ba7-8ec8-3241-aa3c-67bb2f7856a2
Java EE 7 简明教...
浏览量:36810
社区版块
存档分类
最新评论

[NIO.2] 第二十七篇 新建、读取和写出文件

阅读更多
对文件来说,可能最常用的操作就是创建、读取和写出。NIO.2 提供了丰富的方法来完成这些任务。本文从简单的小文件操作开始,最后以缓冲和非缓冲流的操作结束。

流分为输入流和输出流(可以输出到任何地方,比如硬盘或内存)。流支持不同类型的数据,比如字符串、字节、原始数据类型、本地化字符、对象等。使用非缓冲流,读和写的操作直接依赖底层文件系统,使用缓冲流,数据从内存的缓冲区读取,只有缓冲区空了之后才会调用本地方法进行读取。同样,缓冲输出流也是先将数据写出缓冲区,当缓冲区满了之后才会使用本地方法写出。如果没有等到缓冲区满就需要写出数据,那么可以使用 flush 方法。

使用标准参数 OpenOption

在 NIO.2 中,创建、读取和写出都支持一个可选的 OpenOption 参数,使用它来设置怎样打开和读取文件。实际上,OpenOption 是 java.nio.file 包中的一个接口,它有两个实现:LinkOption (还记得著名的 NOFOLLOW_LINKS 枚举常量吗?)和 StandardOpenOption 类。StandardOpenOption 提供了以下的枚举常量:

  •     READ - 打开文件进行读取访问
  •     WRITE - 打开文件进行写出访问
  •     CREATE - 如果文件不存在则创建新文件
  •     CREATE_NEW - 创建新文件,如果文件已存在,则抛出异常
  •     APPPEND - 在文件末尾添加数据(与 WRITE 和 CREATE 结合使用)
  •     DELETE_ON_CLOSE - 当流关闭的时候删除文件(用于删除临时文件)
  •     TRUNCATE_EXISTING - 将文件截断为 0 个字节(与 WRITE 结合使用)
  •     SPARSE - 新创建的文件是 Sparse 文件
  •     SYNC - 保持文件内容和元数据同步
  •     DSYNC - 保持文件内容异步


在本文中,将会演示一些上面的枚举常量的用法。

创建新文件

可以使用 Files.createFile() 方法来创建新文件,这个方法接受一个 Path 类型的参数,并且接受一个可选的 FileAttribute<?> 参数用于在创建文件的时候设置文件属性。调用后返回新创建的文件。下面的代码段将演示在  C:\rafaelnadal\tournaments\2010 目录(此目录必须存在)下创建 SonyEricssonOpen.txt 文件(文件必须不存在,如果已经存在,则会抛出 FileAlreadyExistsException 异常),并使用默认属性。

Path newfile = FileSystems.getDefault(). 
                           getPath("C:/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt"); 
… 
try { 
    Files.createFile(newfile); 
} catch (IOException e) { 
    System.err.println(e); 
}


也可以在创建文件的时候设置属性,下面的代码演示了如何在  POSIX 文件系统上创建文件,并设置访问权限:

Path newfile = FileSystems.getDefault(). 
               getPath("/home/rafaelnadal/tournaments/2010/SonyEricssonOpen.txt"); 

Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-------"); 
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms); 
try { 
    Files.createFile(newfile, attr); 
} catch (IOException e) { 
    System.err.println(e); 
}


随后你将会看到,这并不是创建文件的唯一方式。

写出小文件

NIO.2 提供了优雅的方式来写出小的二进制或文本文件。使用 Files.write() 方法写出文件,这个方法会打开文件用于写出(如果文件不存在会先创建文件)或者将常规文件截取为 0 字节后写出。当所有字节和行写完后,这个方法会关闭文件(即使出现 I/O 错误或异常也会关闭)。简单说来,这个方法相当于使用了  CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 参数。

调用 write() 写出字节

可以使用 Files.write() 来写出字节,这个方法接受一个 Path 类型的参数,一个用于写出的字节数组,和一个可选的文件打开参数(OpenOption)。这个方法会返回写出文件的 Path 对象。

下面的代码段将演示如何写出字节数组,并且使用了默认的打开参数。(文件名为 ball.png,将会被写出到 C:\rafaelnadal\photos 目录):

Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png"); 
… 
byte[] ball_bytes = new byte[]{            
(byte)0x89,(byte)0x50,(byte)0x4e,(byte)0x47,(byte)0x0d,(byte)0x0a,(byte)0x1a,(byte)0x0a, 
(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x0d,(byte)0x49,(byte)0x48,(byte)0x44,(byte)0x52, 
(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x10, 
(byte)0x08,(byte)0x02,(byte)0x00,             
… 
(byte)0x49,(byte)0x45,(byte)0x4e,(byte)0x44,(byte)0xae,(byte)0x42,(byte)0x60,(byte)0x82  
}; 

try { 
    Files.write(ball_path, ball_bytes); 
} catch (IOException e) { 
    System.err.println(e); 
}


现在,你检查目录,就可以看到创建的 ball.png 文件。

如果你想使用字节的方式来写出字符串(String),那么先要将字符串转换为字节数组,下面的代码将在  C:\rafaelnadal\wiki 目录下写出 wiki.txt 文件:

Path rf_wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt"); 
… 
String rf_wiki = "Rafael \"Rafa\" Nadal Parera (born 3 June 1986) is a Spanish professional 
tennis " + "player and a former World No. 1. As of 29 August 2011 (2011 -08-29)[update], he is 
ranked No. 2 " + "by the Association of Tennis Professionals (ATP). He is widely regarded as 
one of the greatest players " + "of all time; his success on clay has earned him the nickname 
\"The King of Clay\", and has prompted " + "many experts to regard him as the greatest clay 
court player of all time. Some of his best wins are:"; 

try { 
    byte[] rf_wiki_byte = rf_wiki.getBytes("UTF-8"); 
    Files.write(rf_wiki_path, rf_wiki_byte); 
} catch (IOException e) { 
    System.err.println(e); 
}


调用 write() 按行写出

可以使用 Files.write() 方法按行写出文件,方法会在每行的结尾根据系统添加一个行结束符(line.separator 系统属性)。这个方法接受一个 Path 类型的参数,一个可迭代的字符序列集合,一个用于编码的参数和一个可选的文件打开参数。这个方法会返回写出的文件 Path 对象。

下面的代码将演示如何按行写出文件(实际上,会在上节创建的 wiki.txt 文件后面添加内容)。

Path rf_wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt");
…
Charset charset = Charset.forName("UTF-8");
ArrayList<String> lines = new ArrayList<>();
lines.add("\n");
lines.add("Rome Masters - 5 titles in 6 years");
lines.add("Monte Carlo Masters - 7 consecutive titles (2005-2011)");
lines.add("Australian Open - Winner 2009");
lines.add("Roland Garros - Winner 2005-2008, 2010, 2011");
lines.add("Wimbledon - Winner 2008, 2010");
lines.add("US Open - Winner 2010");

try {
Files.write(rf_wiki_path, lines, charset, StandardOpenOption.APPEND);
} catch (IOException e) {
System.err.println(e);
}


读取小文件

NIO.2 提供了快速读取小文件的方法。使用  Files.readAllBytes() 和Files.readAllLines() 方法读取文件。这个方法在读取完成后会自动关闭流(即使出现 I/O 异常或错误也会关闭)。

调用 readAllBytes() 方法读取数据

Files.readAllBytes() 方法将整个文件读入字节数组中。下面的代码将演示读取前面创建的  ball.png 文件(文件必须存在)到字节数组:
Path ball_path = Paths.get("C:/rafaelnadal/photos", "ball.png"); 
… 
try { 
    byte[] ballArray = Files.readAllBytes(ball_path);             
} catch (IOException e) { 
    System.out.println(e); 
}


如果你想验证返回的字节是否正确,你可以将返回的字节写出到相同目录的 bytes_to_ball.png 文件中:

… 
Files.write(ball_path.resolveSibling("bytes_to_ball.png"), ballArray); 
…


或者可以通过下面的 ImageIO 方式写出 PNG 图片文件:

BufferedImage bufferedImage = ImageIO.read(new ByteArrayInputStream(ballArray)); 
ImageIO.write(bufferedImage, "png", (ball_path.resolveSibling("bytes_to_ball.png")).toFile());


readAllBytes() 方法也可以用于读取文本文件,这时,读取的字节需要转换成字符串,看看下面的例子(你可以使用任何其它编码):

… 
try { 
    byte[] wikiArray = Files.readAllBytes(wiki_path); 
    String wikiString = new String(wikiArray, "ISO-8859-1"); 
    System.out.println(wikiString); 
} catch (IOException e) { 
    System.out.println(e); 
}


注意:如果文件太大(超过 2GB),那么字节数组的长度将会超出上限,会抛出 OutOfMemory 异常。这依赖于 JVM 的 Xmx 参数:在 32 位 JVM 上,不能超过 2GB(通常使用默认,不超过 256 MB)。在 64 位 JVM 上,可以大一些——十几 GB。

调用 readAllLines() 方法读取文件

使用 readAllLines() 方法会按行返回 String 类型的 List,方便进行循环取值(传递给这个方法的 Path 对象用于指定被读取文件,CharSet 用于设置解码的编码格式):

Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt"); 
… 
Charset charset = Charset.forName("ISO-8859-1"); 
try { 
    List<String> lines = Files.readAllLines(wiki_path, charset); 
    for (String line : lines) { 
         System.out.println(line); 
    } 
} catch (IOException e) { 
    System.out.println(e); 
}


按照官方文档,这个方法会识别以下的行结束符:

  •     \u000A\u000D - 换行回车
  •     \u000A - 换行
  •     \u000D - 回车


使用缓冲流

对于大多数操作系统来说,进行读写操作都是比较消耗资源的操作。NIO.2 提供了两个方法来进行缓冲区读写:Files.newBufferedReader() 和 Files.newBufferedWriter(),这两个方法都接受 Path 类型的对象,并返回老的 JDK 1.1 中的 BufferedReader 或 BufferedWriter 对象。

使用 newBufferedWriter() 方法

newBufferedWriter() 参数为一个 Path 类型的对象,一个 Charset 对象用于编码,一个可选的文件打开方式选项,返回一个新的 BufferedWriter 对象。这个方法将打开文件用于写出(如果文件不存在将会创建文件)或者将已存在的文件截取为 0 字节。简短说来,这个方法默认情况下相当于使用了 CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 属性。

下面的代码将在前面创建的 wiki.txt 文件后面添加内容:

Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt"); 
… 
Charset charset = Charset.forName("UTF-8"); 
String text = "\nVamos Rafa!"; 
try (BufferedWriter writer = Files.newBufferedWriter(wiki_path, charset,  
                                                                 StandardOpenOption.APPEND)) { 
     writer.write(text); 
} catch (IOException e) { 
     System.err.println(e); 
}


使用 newBufferedReader() 方法

newBufferedReader() 方法可用于通过缓冲区读取文件。它的参数为一个 Path 类型的对象,一个 Charset 用于解码。它将返回一个 BufferedReader 类型的对象。

下面的代码将演示使用 UTF-8 的方式读取 wiki.txt:

Path wiki_path = Paths.get("C:/rafaelnadal/wiki", "wiki.txt"); 
… 
Charset charset = Charset.forName("UTF-8"); 
try (BufferedReader reader = Files.newBufferedReader(wiki_path, charset)) { 
     String line = null; 
     while ((line = reader.readLine()) != null) { 
             System.out.println(line); 
     } 
} catch (IOException e) { 
     System.err.println(e); 
}


如果按照前文的例子一步步运行下来,那么运行结果将会是:

Rafael "Rafa" Nadal Parera (born 3 June 1986) is a Spanish professional tennis player and a 
former World No. 1. As of 29 August 2011 (2011 -08-29)[update], he is ranked No. 2 by the 
Association of Tennis Professionals (ATP). He is widely regarded as one of the greatest 
players of all time; his success on clay has earned him the nickname "The King of Clay", and 
has prompted many experts to regard him as the greatest clay court player of all time. Some 
of his best wins are: 
Rome Masters - 5 titles in 6 years 
Monte Carlo Masters - 7 consecutive titles (2005-2011) 
Australian Open - Winner 2009 
Roland Garros - Winnner 2005-2008, 2010, 2011 
Wimbledon - Winner 2008, 2010 
US Open - Winner 2010 
Vamos Rafa!


使用非缓冲流

NIO.2 提供了使用非缓冲流的方法,可以用于直接读取或通过 java.io 的 API 封装成缓冲流使用。使用非缓冲流的方法是  Files.newInputStream() 和 Files.newOutputStream()。

使用 newOutputStream() 方法

newOutputStream() 参数为一个 Path 对象和一个可选的用于配置文件打开方式的 OpenOption 配置选项。它返回一个新的线程安全的非缓冲输出流 OutputStream。这个方法将打开文件用于写出(如果文件不存在将会创建文件)或者将已存在的文件截取为 0 字节。简短说来,这个方法默认情况下相当于使用了 CREATE、TRUNCATE_EXISTING、和 WRITE 的 OpenOption 属性。

下面的代码将会写出 "Racquet: Babolat AeroPro Drive GT" 文本内容到文件 C:\rafaelnadal\equipment\racquet.txt,因为没有指定 OpenOption,所以文件不存的话会自动创建:

Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt"); 
String racquet = "Racquet: Babolat AeroPro Drive GT"; 
 
byte data[] = racquet.getBytes(); 
try (OutputStream outputStream = Files.newOutputStream(rn_racquet)) { 
     outputStream.write(data); 
} catch (IOException e) { 
     System.err.println(e); 
}


另外,如果你决定要使用缓冲流来取代非缓冲流,可以使用 java.io API 提供的方法。看看下面的代码,将会在 racquet.txt 文件(文件必须存在)后面添加“String: Babolat RPM Blast 16” 文本。

Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt"); 
String string = "\nString: Babolat RPM Blast 16";    
 
try (OutputStream outputStream = Files.newOutputStream(rn_racquet, StandardOpenOption.APPEND); 
     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream))) { 
      writer.write(string); 
} catch (IOException e) { 
     System.err.println(e); 
}


使用  newInputStream() 方法

newInputStream() 的参数为一个 Path 对象和一个可选的用于配置文件打开方式的 OpenOption 配置选项。它返回一个新的线程安全的 InputStream 对象。默认情况下,这个方法是用的是 OpenOption.READ 选项。

下面的代码将读取 racquet.txt 文件(文件必须存在)的内容:

Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt"); 
… 
int n;      
try (InputStream in = Files.newInputStream(rn_racquet)) { 
     while ((n = in.read()) != -1) { 
       System.out.print((char)n);                 
    } 
} catch (IOException e) { 
    System.err.println(e); 
}


你也可以使用 InputStream 的 read() 方法将数据读取到用于缓冲的字节数组中。你可以将上面的代码改写为(记住,你的处理对象依然是非缓冲输入流):

Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt"); 
… 
int n;      
byte[] in_buffer = new byte[1024]; 
try (InputStream in = Files.newInputStream(rn_racquet)) { 
     while ((n = in.read(in_buffer)) != -1) { 
            System.out.println(new String(in_buffer)); 
     } 
} catch (IOException e) { 
     System.err.println(e); 
}

注:调用 read(in_buffer) 方法和调用 read(in_buffer,0,in_buffer.length) 方法是同样的效果。

你也可以通过 java.io API 将非缓冲输入流转换为缓冲输入流,下面的代码和上面的代码运行结果相同,但是更有效率:

Path rn_racquet = Paths.get("C:/rafaelnadal/equipment", "racquet.txt"); 
… 
try (InputStream in = Files.newInputStream(rn_racquet); 
     BufferedReader reader = new BufferedReader(new InputStreamReader(in))) { 
     String line = null; 
     while ((line = reader.readLine()) != null) { 
             System.out.println(line); 
     } 
} catch (IOException e) { 
     System.err.println(e); 
}


上面三段代码输出的结果都是相同的:

Racquet: Babolat AeroPro Drive GT 
String: Babolat RPM Blast 16


文章来源:http://www.aptusource.org/2014/04/nio-2-creating-reading-and-writing-files/
0
0
分享到:
评论

相关推荐

    JDK1.7 之java.nio.file.Files 读取文件仅需一行代码实现

    JDK1.7 之 java.nio.file.Files 读取文件仅需一行代码实现 java.nio.file.Files 类是 JDK1.7 中引入的新的文件操作类,该类包含了许多有用的方法来操作文件。其中,Files.readAllBytes(Path) 方法可以将整个文件...

    java NIO.zip

    Java NIO支持多种类型的通道,包括文件通道(FileChannel)、套接字通道(SocketChannel)和服务器套接字通道(ServerSocketChannel)等。通道可以同时进行读写操作,并且可以实现异步读写。 2. **缓冲区(Buffers...

    Java IO, NIO and NIO.2(Apress,2015)

    Java I/O, NIO, and NIO.2 is a power-packed book that accelerates your mastery of Java's various I/O APIs. In this book, you'll learn about classic I/O APIs (File, RandomAccessFile, the stream classes ...

    Java IO, NIO and NIO.2

    本书《Java IO, NIO and NIO.2》旨在深入浅出地介绍这些机制,同时书中内容均为英文。接下来将详细介绍这些知识点。 **Java IO** Java IO是Java语言中传统的输入输出处理方式。它基于流的概念,包括字节流和字符流...

    NIO.2 入门,第 2 部分: 文件系统 API

    在Java的NIO.2(New IO 2.0)框架中,文件系统API提供了更加高效和灵活的方式来处理文件和目录。本篇我们将深入探讨NIO.2中的几个关键概念和类,包括`Files`, `Path`, `FileAttributes`, `DirectoryWatcher`以及`...

    nio.rar_FastCopyFile.java_NIO_UseFloatBuffer.java_java nio_文件锁

    Java NIO(New Input/Output)是Java标准库中提供的一种I/O模型,与传统的BIO( Blocking I/O)相比,NIO具有更好的性能和更高的灵活性。NIO的核心组件包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。...

    Pro Java 7 NIO.2.pdf

    根据提供的文件信息,“Pro Java 7 NIO.2.pdf”由Anghel Leonard于2011年编写,主要介绍了Java 7中的新输入/输出(NIO)API,特别是NIO.2(JSR 203)所带来的增强功能。这本书通过一系列章节详细讲解了如何使用NIO.2...

    JAVA NIO 按行读取大文件支持 GB级别-修正版

    本类,是专门为了处理大文件,按行读取开发的类。 采用读文件的缓存 fbb 1024*5 行缓存 bb 256 字节 设计思想: 每次通过nio读取字节到 fbb中 然后对fbb自己中的内容进行行判断即 10 回车 13 行号 0 文件结束 ...

    Java IO, NIO and NIO.2 原版pdf by Friesen

    New I/O (NIO), and NIO.2 categories. You learn what each category offers in terms of its capabilities, and you also learn about concepts such as paths and Direct Memory Access. Chapters 2 through 5 ...

    JAVA NIO 按行读取大文件,支持 GB级别

    本类,是专门为了处理大文件,按行读取开发的类。 采用读文件的缓存 fbb 1024*5 行缓存 bb 256 字节 设计思想: 每次通过nio读取字节到 fbb中 然后对fbb自己中的内容进行行判断即 10 回车 13 行号 0 文件...

    Java NIO 中英文版 + Pro Java 7 NIO.2

    `Pro Java 7 NIO.2`这本书由Anghel Leonard著,深入探讨了Java NIO.2 API,这是Java 7引入的进一步扩展,包括: 1. **文件系统API增强**:新增了AsynchronousFileChannel,支持异步文件操作,可以在后台线程中执行...

    java nio 包读取超大数据文件

    ### Java NIO 处理超大数据文件的知识点详解 ...综上所述,使用Java NIO处理超大数据文件时,关键是利用好内存映射文件技术和合理的数据读取策略,通过适当的分块和数据解析方法,可以有效地提升读取速度和处理能力。

    Java I/O, NIO and NIO.2

    Java I/O, NIO, 和 NIO.2 是Java平台中处理输入/输出操作的核心组件,对于任何Java开发者来说,理解和掌握这些概念至关重要。本文将深入探讨这些技术,旨在提供一个全面而详尽的概述。 Java I/O(Input/Output)是...

    优雅的操作文件:java.nio.file 库介绍.pdf

    为了解决这些问题,Java引入了`java.nio.file`库,这个库极大地提升了Java在文件操作方面的效率和易用性。 ### 1. Path路径 `java.nio.file.Paths`模块提供静态方法创建`Path`对象,代表文件系统中的路径。`Path`...

    java nio.pdf

    java nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava nio.pdfjava ...

    java NIO 写文件

    在这个主题中,我们将深入探讨Java NIO如何用于写文件,特别是在处理大数据文件时如何提高性能。 首先,理解Java NIO的基本概念非常重要。NIO中的“N”代表“非阻塞”,这意味着在进行I/O操作时,程序可以继续执行...

Global site tag (gtag.js) - Google Analytics