在NIO.2的文件I/O中,Path表示文件系统中的位置。这个Path类似于java.io.File类对象,并不表示一个已经存在的实际文件或目录,所以如果你想创建一个Path来表示即将要创建的目录或文件是完全可以的。但是要注意,如果这个文件不存在,读取时就会出现错误。
在File中已经介绍过,File转换为Path需要调用File类中的toPath()方法,同样,Path也可以转换为File,调用toFile()方法即可。java.nio.file.Path接口中定义了一些文件操作常用的方法。如创建Path、获取路径的相关信息等,如下:
Path listing = Paths.get("\\foo\\x");// 创建一个Path
System.out.println(listing.isAbsolute());// false 是否为绝对路径
System.out.println(listing.toAbsolutePath());// C:\foo\x
System.out.println( listing.getFileName() );// 获取文件名
System.out.println("Number of Name Elements in the Path "+ listing.getNameCount() );// 2
System.out.println("Parent Path " + listing.getParent() );// \foo
System.out.println("Root of Path " + listing.getRoot() );// // 从根到第二个元素之间的子路径,离根最近的为0,而离根最远的元素为getNameCount-1
System.out.println("Subpath from Root, 2 elements deep "+ listing.subpath(0, 2) );// foo\x
Paths类是一个工具类,提供返回一个路径的辅助方法,源代码如下:
public final class Paths {
private Paths() { }
//more
public static Path get(String first, String... more) {
return FileSystems.getDefault().getPath(first, more);
}
public static Path get(URI uri) {
String scheme = uri.getScheme();
if (scheme == null)
throw new IllegalArgumentException("Missing scheme");
// check for default provider to avoid loading of installed providers
if (scheme.equalsIgnoreCase("file"))
return FileSystems.getDefault().provider().getPath(uri);
// try to find provider
for (FileSystemProvider provider: FileSystemProvider.installedProviders()) {
if (provider.getScheme().equalsIgnoreCase(scheme)) {
return provider.getPath(uri);
}
}
throw new FileSystemNotFoundException("Provider \"" + scheme + "\" not installed");
}
}可以调用如上的两个方法获取Path对象。获取到后就可以进行操作了。下面来看一下查找和遍历目录树的例子。
public class TestDirectoryStream {
public static void main(String[] args) throws IOException {
Path listing = Paths.get("C:\\foo\\x");
List l=TestDirectoryStream.listSourceFiles(listing);
for(int i=0;i<l.size();i++ ){
System.out.println(l.get(i));// 输出
}
}
static List<Path> listSourceFiles(Path dir) throws IOException {
List<Path> result = new ArrayList<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir,"*.{c,h,cpp,hpp,txt}")) {
for (Path entry : stream) { // stream实例可以迭代
result.add(entry);
}
} catch (DirectoryIteratorException ex) {
throw ex.getCause();
}
return result;
}
}查找并输出x目录下的所有符合glob模式匹配的文件。调用的主要方法就是Files类下的newDirectoryStream()方法,源代码如下:public static DirectoryStream<Path> newDirectoryStream(Path dir, String glob) throws IOException {
// avoid creating a matcher if all entries are required.
if (glob.equals("*"))
return newDirectoryStream(dir);
// create a matcher and return a filter that uses it.
FileSystem fs = dir.getFileSystem();
final PathMatcher matcher = fs.getPathMatcher("glob:" + glob);
DirectoryStream.Filter<Path> filter = new DirectoryStream.Filter<Path>() {
@Override
public boolean accept(Path entry) {
return matcher.matches(entry.getFileName());
}
};
return fs.provider().newDirectoryStream(dir, filter);
}或者还可以遍历目录树并查找文件:
public class TestFileVisitor {
public static void main(String[] args) throws IOException {
Path startingDir = Paths.get("c:\\foo");
Files.walkFileTree(startingDir, new FindJavaVisitor());
}
private static class FindJavaVisitor extends SimpleFileVisitor<Path> {
public FileVisitResult preVisitDirectory(Path file, BasicFileAttributes attrs) {
return FileVisitResult.CONTINUE;
}
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (file.toString().endsWith(".txt")) {
System.out.println(file.getFileName());
}
return FileVisitResult.CONTINUE;
}
}
}用到的主要方法就是Files的walkFileTree(Path startingDir,FileVisitor<? super Path> visitor)。由于FileVisitor类接口中定义的方法很多,为了减少工作量,我们可以继承SimplefileVisitor类来实现部分方法,然后在这些方法中定义我们自己的功能,如visitFile()方法中将扩展名为.txt的文件输出。如果要针对某个文件进行如下操作时,主要是通过提供的Files工具类来完成。
1、创建、删除
Path p=Paths.get("c:\\foo\\pp.txt");
//Files.createFile(p);// 创建pp.txt文件
Files.delete(p);// 删除pp.txt文件2、移动、复制、重命名文件
Path p1=Paths.get("c:\\foo\\pp.txt");
Path p2=Paths.get("c:\\foo\\x\\pp2.txt");
Path p3=Paths.get("c:\\foo\\x\\pp3.txt");
Files.copy(p1, p2);// 复制pp.txt文件到x目录下并且重新命名为pp2.txt
Files.move(p1, p3);// 将pp.txt文件移动到x目录下并且重新命名为pp3.txt3、文件属性的处理
try {
Path zip = Paths.get("C:\\foo");
System.out.println(zip.toAbsolutePath().toString());// C:\foo
System.out.println(Files.getLastModifiedTime(zip));// 2014-01-26T09:46:07.696941Z
System.out.println(Files.size(zip));// 4096
System.out.println(Files.isSymbolicLink(zip));// false
System.out.println(Files.isDirectory(zip));// true
// 执行批量读取
System.out.println(Files.readAttributes(zip, "*"));
} catch (IOException ex) {
System.out.println("Exception" + ex.getMessage());
}最后一行运行结果如下:
{lastModifiedTime=2014-01-26T09:46:07.696941Z, fileKey=null, isDirectory=true, lastAccessTime=2014-01-26T09:46:07.696941Z, isOther=false, isSymbolicLink=false, isRegularFile=false, creationTime=2013-12-02T11:54:55.062597Z, size=4096}
4、读取、写入文件
在新的NIO中,可以对文件进行行读取,也可以按照行进行写入,使用到的主要方法就是Files类中的newBufferedReader()和newBufferedWriter(),源代码如下:
public static BufferedReader newBufferedReader(Path path, Charset cs) throws IOException {
CharsetDecoder decoder = cs.newDecoder();
Reader reader = new InputStreamReader(newInputStream(path), decoder);
return new BufferedReader(reader);
}
public static BufferedWriter newBufferedWriter(Path path, Charset cs,OpenOption... options)throws IOException {
CharsetEncoder encoder = cs.newEncoder();
Writer writer = new OutputStreamWriter(newOutputStream(path, options), encoder);
return new BufferedWriter(writer);
}
可以看到,其实是通过装饰器模式对流进行一步步的封装,最后得到了缓冲流。编写的测试程序如下:
Path logFile=Paths.get("C:\\foo\\mm.txt");
try(BufferedReader reader=Files.newBufferedReader(logFile, StandardCharsets.UTF_8)){
String line;
while((line=reader.readLine())!=null){
System.out.println(line);
}
}注意,虽然StandardCharsets.UTF-8指定了编码,但是如果有汉字,仍然会报出java.nio.charset.MalformedInputException异常。 Path logFile=Paths.get("C:\\foo\\mm.txt");
try(BufferedWriter writer=Files.newBufferedWriter(logFile, StandardCharsets.UTF_8,StandardOpenOption.WRITE)){
writer.write("1234");
writer.write("lkajd");
}
如上演示了行的读取和写入,其实在Files类中还提供了两个辅助方法readAllLines()和readAllBytes()方法,用于读取全部行和全部字节。源代码如下:
public static byte[] readAllBytes(Path path) throws IOException {
long size = size(path);
if (size > (long)Integer.MAX_VALUE)
throw new OutOfMemoryError("Required array size too large");
try (InputStream in = newInputStream(path)) {
return read(in, (int)size);
}
}
public static List<String> readAllLines(Path path, Charset cs) throws IOException {
try (BufferedReader reader = newBufferedReader(path, cs)) {
List<String> result = new ArrayList<>();
for (;;) {
String line = reader.readLine();
if (line == null)
break;
result.add(line);
}
return result;
}
}
可以看到readAllLines()方法其实就是按行读取内容,然后存储到List中并返回。而readAllBytes()读取的文件字节数不能大于Integer.MAX_VALUE,否则会抛出异常。调用read()方法,源代码如下:
private static byte[] read(InputStream source, int initialSize) throws IOException{
int capacity = initialSize;
byte[] buf = new byte[capacity];
int nread = 0;
int rem = buf.length;
int n;
// read to EOF which may read more or less than initialSize (eg: file
// is truncated while we are reading)
while ((n = source.read(buf, nread, rem)) > 0) {
nread += n;
rem -= n;
assert rem >= 0;
if (rem == 0) {
// need larger buffer
int newCapacity = capacity << 1;
if (newCapacity < 0) {
if (capacity == Integer.MAX_VALUE)
throw new OutOfMemoryError("Required array size too large");
newCapacity = Integer.MAX_VALUE;
}
rem = newCapacity - capacity;
buf = Arrays.copyOf(buf, newCapacity);
capacity = newCapacity;
}
}
return (capacity == nread) ? buf : Arrays.copyOf(buf, nread);
}
其实最终还是调用了字节流的read()方法进行读取,然后将读取到的结果拷贝到一个更大的缓存数组中。在NIO中,可以使用SeekableByteChannel来灵活的处理文件,类似于RandomAccessFile类提供的一些方法,但是这里提出了一种新的I/O抽象-通道。SeekableByteChannel接口的定义如下:
public interface SeekableByteChannel extends ByteChannel{
int read(ByteBuffer dst) throws IOException;
int write(ByteBuffer src) throws IOException;
/**
* Returns this channel‘s position.
*/
long position() throws IOException;
/**
* Sets this channel‘s position.
*/
SeekableByteChannel position(long newPosition) throws IOException;
/**
* Returns the current size of entity to which this channel is connected.
*/
long size() throws IOException;
/**
* Truncates the entity, to which this channel is connected, to the given
* size.
*/
SeekableByteChannel truncate(long size) throws IOException;
}可以改变通道的位置和大小,所以处理文件内容更加灵活。这个接口的一个抽象实现类为java.nio.channels.FileChannel。通过调用open()方法来获取FileChannel的具体实例。编写一个测试实例,如下:
Path logFile=Paths.get("C:\\temp.txt");
ByteBuffer buffer=ByteBuffer.allocate(1024);
FileChannel channel=FileChannel.open(logFile, StandardOpenOption.READ);
channel.read(buffer,channel.size()-100);// 读取文件最后的100个字符
buffer.flip();//此行语句一定要有
while (buffer.hasRemaining()) {
System.out.print((char)buffer.get());
} 输出文件最后100个字符。
原文:http://blog.csdn.net/mazhimazh/article/details/18809059