转自 开发者的天空
本文中我们来讨论在NIO2 中怎样创建文件 、读取文件和写文件。NIO2提供了多种创建 文件的方法,使得我们在创建文件的时候就可以指定文件的某些初始属性。例如在支持POSIX的文件系统上指定文件的所有者,访问权限等。关于文件的属性, 请看上一篇文章Java SE 7新特性之文件操作 (5) - 管理元数据
创建文件
可以调用createFile(FileAttribute<?>)方法创建一个空文件。该方法的参数就是文件的初始属性。下面的例子是怎样 在创建文件的时候赋予该文件某些权限的属性:
如 果在调用该方法的时候没有传入任何参数,那么创建的文件将具有缺省的文件属性。下面的代码创建了一个具有缺省文件属性的文件:
Path file = ...; try { file.createFile(); //Create the empty file with default permissions, etc. } catch (FileAlreadyExists x) { System.err.format("file named %s already exists%n", file); } catch (IOException x) { //Some other sort of failure, such as permissions. System.err.format("createFile error: %s%n", x); }
如 果要创建的文件已经存在,该方法会抛出异常。
注意在调用createFile方法时,检查文件是否存在和创建具有特定的属性的文件是在同一个原子操作中。
还可以使用newOutputSteam方法来创建文件,在本文的后面我们会讲到怎样使用newOutputStream方法来创建文件。
通过Stream I/O读文件
我们可以通过newInputStream(OpenOption...)方法打开和读取文件。这个方法返回一个unbuffered输入流(input stream),我们可以用它来从文件中读取字节内容。
Path file = ...; InputStream in = null; try { in = file.newInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(in)); String line = null; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException x) { System.err.println(x); } finally { if (in != null) in.close(); }
注 意该方法接受可变个数的参数,参数类型为OpenOption,指定了文件怎样打开。如果不传入参数,则使用默认的READ方式打开。READ方式是所有 的实现都支持的方式。有一些实现也支持其他的打开方式。
如果传入的OpenOption或其组合不正确,会抛出异常。如果程序没有读权限或I/O错误,也会抛出异常。
Creating and Writing a File by Using Stream I/O
使用Stream I/O来创建和写文件
我们可以使用newOutputStream方法来创建文件、扩展文件或覆盖已有文件。这个方法为了写文件而打开或创建文件,该方法返回一个 unbuffered的输出流(output stream)。newOutputStream方法有两种形式:
这两种形式都接受一组OpenOption作为参数,第二种形式还允许指定初始的文件属性。这个方法支持的StandardOpenOption有:
如果没有指定OpenOption,该方法的行为是:如果文件不存在,则创建新文件;如果文件存在,那么截断它。也就是说缺省的选择是CREATE和 TRUNCATE_EXISTING选项的组合。
下面的代码打开一个日志 文件,如果文件不存在,则创建一个新文件。如果文件 存在,这将新的内容扩展到文件尾部。
import static java.nio.file.StandardOpenOption.*; Path logfile = ...; //Convert the string to a byte array. String s = ...; byte data[] = s.getBytes(); OutputStream out = null; try { out = new BufferedOutputStream(logfile.newOutputStream(CREATE, APPEND)); ... out.write(data, 0, data.length); } catch (IOException x) { System.err.println(x); } finally { if (out != null) { out.flush(); out.close(); } }
使用Channel I/O来读写文件
Stream I/O每次读取一个字符,Channel I/O每次读取一个缓冲块的数据。ByteChannel接口提供了基本的读写功能。SeekableByteChannel扩展了 ByteChannel并提供了维护一个channel中的位置并改变该位置的能力。SeekableByteChannel还支持截断文件和查询文件大 小的功能。
移动到文件中不同的位置,从该位置开始读或写的能力使我们可以随机访问文件 。有两种形式的 newByteChannel方法可以用来读或写文件,这两种形式和newOutputStream方法一样。
这两个方法都允许指定OpenOption,newOutputStream所支持的选择这里也支持,而且这里还支持另外一个选项READ,因为 SeekableByteChannel既支持读也支持写。
如果选项是READ,那么该channel就是为了读访问打开。如果选项是WRITE或APPEND,则该channel就是为了写访问打开。如果没有指 定,该channel默认是为了读打开。
下面的代码从文件中读取内容并输出到控制台上:
SeekableByteChannel sbc = null; try { sbc = file.newByteChannel(); //Defaults to READ ByteBuffer buf = ByteBuffer.allocate(10); //Read the bytes with the proper encoding for this platform. //If you skip this step, you might see something that looks like Chinese //characters when you expect Latin-style characters. String encoding = System.getProperty("file.encoding"); while (sbc.read(buf) > 0) { buf.rewind(); System.out.print(Charset.forName(encoding).decode(buf)); buf.flip(); } } catch (IOException x) { System.out.println("caught exception: " + x); } finally { if (sbc != null) sbc.close(); }
下 面的代码是为了UNIX或其他支持POSIX的文件系统编写的。这段代码创建一个新的日志文件或者扩展原有的日志文件,该日志文件创建时指定了访问权限 (所有者有读写权限,同组用户只有读权限,其他用户没有读权限)。
import static java.nio.file.StandardCopyOption.*; //Create the set of options for appending to the file. Set<OpenOptions> options = new HashSet<OpenOption>(); options.add(APPEND); options.add(CREATE); //Create the custom permissions attribute. Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r------"); FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms); //Convert the string to a ByetBuffer. String s = ...; byte data[] = s.getBytes(); ByteBuffer bb = ByteBuffer.wrap(data); SeekableByteChannel sbc = null; try { sbc = file.newByteChannel(options, attr); sbc.write(bb); } catch (IOException x) { System.out.println("exception thrown: " + x); } finally { if (sbc != null) sbc.close(); }