概述
文件
- 前言
- File类
- 文件和目录路径
- 选取路径部分片段
- 路径分析
- Paths的增减修改
- 目录
- 文件系统
- 路径监听
- 文件查找
- 文件读写
- 本章小结
前言
IO流,也就是输入输出流,从文件出发到文件结束,至始至终都离不开文件,所以IO流还得从文件File类讲起。
File类
专门对文件进行操作的类,只能对文件本身进行操作,不能对文件内容进行操作,主要用于文件和目录的创建、查找和删除等操作。
public class NewFileTest {
public static void main(String[] args) throws IOException {
File file = new File("C:\mnt\test1");
String[] list = file.list();//获取目录下的文件名
File[] files = file.listFiles();//获取目录下的文件
System.out.println("path:"+file.getPath());//获取路径
System.out.println("absolutePath:"+file.getAbsolutePath());//获取绝对路径
System.out.println("parent:"+file.getParent());//获取父文件
file = new File("C:\mnt\test\t1");
System.out.println("name:"+file.getName());//获取文件名
System.out.println("isAbsolute:"+file.isAbsolute());//判断是不是绝对路径
System.out.println("isHidden:"+file.isHidden());//判断是不是隐藏文件
System.out.println("isDirectory:"+file.isDirectory());//判断是不是文件夹
System.out.println("isFile:"+file.isFile());//判断是不是文件
System.out.println("exists:"+file.exists());//判断文件或文件夹是否存在
System.out.println("mkdir:"+file.mkdir());//创建单个文件夹(父级文件必须存在)
System.out.println("mkdirs:"+file.mkdirs());//创建多层级文件夹
file = new File("C:\mnt\test\t1\t.txt");
System.out.println("createNewFile:"+file.createNewFile());//创建文件
System.out.println("setReadOnly:"+file.setReadOnly());//设置文件为只读
System.out.println("canRead:"+file.canRead());//判断文件是否可读
System.out.println("canWrite:"+file.canWrite());//判断文件是否可写
System.out.println("delete:"+file.delete());//立即删除
file.deleteOnExit();//待jvm终止才进行删除
}
}
执行结果
path:C:mnttest1
absolutePath:C:mnttest1
parent:C:mnt
name:t1
isAbsolute:true
isHidden:false
isDirectory:false
isFile:false
exists:false
mkdir:false
mkdirs:true
createNewFile:true
setReadOnly:true
canRead:true
canWrite:false
delete:true
由于 I/O 糟糕的设计以至于 很少有人能够在不依赖其他参考代码的情况下完成打开文件的操作。好像 Java 设计者终于意识到了 Java 使用者多年来的痛苦,在 Java7 中对此引入了巨大的改进。这些新元素被放在 java.nio.file
包下面,过去人们通常把 nio 中的 n 理解为 new 即新的 io,现在更应该当成是 non-blocking 非阻塞 io(io就是input/output输入/输出)。java.nio.file
库终于将 Java 文件操作带到与其他编程语言相同的水平。最重要的是 Java8 新增的 streams 与文件结合使得文件操作编程变得更加优雅。我们将看一下文件操作的两个基本组件:
- 文件或者目录的路径;
- 文件本身。
文件和目录路径
一个 Path 对象表示一个文件或者目录的路径,是一个跨操作系统(OS)和文件系统的抽象,目的是在构造路径时不必关注底层操作系统,代码可以在不进行修改的情况下运行在不同的操作系统上。java.nio.file.Paths
类包含一个重载方法 static get()
,该方法接受一系列 String 字符串或一个统一资源标识符(URI)作为参数,并且进行转换返回一个 Path 对象:
public class NewFileTest {
public static void main(String[] args) throws IOException, URISyntaxException {
// Path path = Paths.get(new URI("D:\mnt"));
// Path path = Paths.get("D:\mnt");
Path path = Paths.get("D:","mnt","test.txt");
System.out.println("转换为绝对路径:"+path.toAbsolutePath());
System.out.println("转换为File:"+path.toFile());
System.out.println("转换为URI:"+path.toUri());
System.out.println("toString:"+path.toString());
System.out.println("toRealPath:"+path.toRealPath());//实际地址
System.out.println("root:"+path.getRoot());//获取根目录
System.out.println("parent:"+path.getParent());//获取父目录
System.out.println("fileName:"+path.getFileName());//获取文件名
System.out.println("isAbsolute:"+path.isAbsolute());//判断是不是绝对路径
System.out.println("isHidden:"+Files.isHidden(path));//判断是不是隐藏文件
System.out.println("isDirectory:"+Files.isDirectory(path));//判断是不是文件夹
System.out.println("isRegularFile:"+Files.isRegularFile(path));//判断是不是文件
System.out.println("exists:"+Files.exists(path));//判断文件或文件夹是否存在
path = Paths.get("D:","mnt","dic");
System.out.println("createDirectory:"+Files.createDirectory(path));//创建单个文件夹(上一层文件夹不存在,则抛异常)
path = Paths.get("D:","mnt","dic","t1");
System.out.println("createDirectories:"+Files.createDirectories(path));//创建多层级文件夹
Path createTempFile = Files.createTempFile("test", ".txt");
System.out.println("createTempFile:"+createTempFile);//创建临时文件,返回临时文件地址
path = Paths.get("D:","mnt","dic","this.txt");
System.out.println("createFile:"+Files.createFile(path));//创建文件
System.out.println("deleteIfExists:"+Files.deleteIfExists(createTempFile));//删除存在的文件
Files.list(Paths.get("D:","mnt")).forEach(item-> System.out.println(item.toString()));//遍历路径下所有文件
}
}
执行结果
转换为绝对路径:D:mnttest.txt
转换为File:D:mnttest.txt
转换为URI:file:///D:/mnt/test.txt
toString:D:mnttest.txt
toRealPath:D:mnttest.txt
root:D:
parent:D:mnt
fileName:test.txt
isAbsolute:true
isHidden:false
isDirectory:false
isRegularFile:true
exists:true
createDirectory:D:mntdic
createDirectories:D:mntdict1
createTempFile:C:UsersShancwAppDataLocalTemptest2955572342747349825.txt
createFile:D:mntdicthis.txt
deleteIfExists:true
D:mntdic
D:mnttest.txt
D:mnttest1.txt
上面的例子,通过使用 Files 工具类(我们接下来将会更多地使用它),可以测试一个文件是否存在,测试是否是一个"普通"文件还是一个目录等等。
选取路径部分片段
Path 对象可以非常容易地生成路径的某一部分:
public class NewFileTest {
public static void main(String[] args) {
Path path = Paths.get("D:\mnt\dic\t1").toAbsolutePath();
System.out.println("getNameCount:"+path.getNameCount());
for (int i = 0; i < path.getNameCount(); i++) {
System.out.println(path.getName(i));
}
// for (Path p:path) {
// //todo
// }
System.out.println("startsWith:"+path.startsWith(path.getRoot()));
System.out.println("endsWith:"+path.endsWith("t1"));
/** Output:
* getNameCount:3
* mnt
* dic
* t1
* startsWith:true
* endsWith:true
*/
}
}
Path 也实现了 Iterable 接口,,因此我们也可以通过增强的 for-each 进行遍历,遍历 Path 对象并不包含根路径,startsWith()
和 endsWith()
可以检测路径的开始和结尾,只有匹配时才会返回true。
路径分析
在文章最开始介绍了一些基本的用法,接着继续介绍:
public class NewFileTest {
public static void main(String[] args) throws IOException, URISyntaxException {
Path path = Paths.get("D:","mnt","test.txt");
System.out.println("isExecutable:"+Files.isExecutable(path));//判断是否是可执行文件
System.out.println("isReadable:"+Files.isReadable(path));//判断是否是可读文件
System.out.println("isWritable:"+Files.isWritable(path));//判断是否是可写文件
System.out.println("isRegularFile:"+Files.isRegularFile(path));//判断是否是常规文件
System.out.println("isSymbolicLink:"+Files.isSymbolicLink(path));//判断地址是不是符号链接
System.out.println("size:"+Files.size(path));//文件大小(字节)
System.out.println("getFileStore:"+Files.getFileStore(path));//获取文件存储位置
System.out.println("getLastModifiedTime:"+Files.getLastModifiedTime(path));//获取文件最后一次修改时间
System.out.println("getOwner:"+Files.getOwner(path));//获取文件所有权信息
System.out.println("probeContentType:"+Files.probeContentType(path));//获取文件内容类型
/** Output:
* isExecutable:true
* isReadable:true
* isWritable:true
* isRegularFile:true
* isSymbolicLink:false
* size:41
* getFileStore:Data (D:)
* getLastModifiedTime:2022-01-23T14:52:32.339137Z
* getOwner:LAPTOP-33SV7QS9Shancw (User)
* probeContentType:text/plain
*/
}
}
Paths的增减修改
我们必须能通过对 Path 对象增加或者删除一部分来构造一个新的 Path 对象。我们使用 relativize()
构造两个路径之间的相对路径,使用 resolve()
添加 Path 的尾路径,使用normalize()
标准化输出。
public class NewFileTest {
public static void main(String[] args) {
Path p = Paths.get("Test.java").toAbsolutePath();
System.out.println(p);
Path resolve = p.resolve("..");
System.out.println(resolve);
System.out.println(resolve.normalize());
Path study = resolve.relativize(Paths.get("D:\abc"));
System.out.println(study);
/** Output:
* D:studyTest.java
* D:studyTest.java..
* D:study
* ......abc
*/
}
}
目录
Files 工具类包含大部分我们需要的目录操作和文件操作方法。
public class NewFileTest {
public static void main(String[] args) throws IOException {
Path p = Paths.get("D:","mnt","test.txt");
Files.walkFileTree(p, new SimpleFileVisitor(){
@Override
public FileVisitResult postVisitDirectory(Object dir, IOException exc) throws IOException {
System.out.println("1 "+dir);
return super.postVisitDirectory(dir, exc);
}
@Override
public FileVisitResult preVisitDirectory(Object dir, BasicFileAttributes attrs) throws IOException {
System.out.println("2 "+dir);
return super.preVisitDirectory(dir, attrs);
}
@Override
public FileVisitResult visitFile(Object file, BasicFileAttributes attrs) throws IOException {
System.out.println("3 "+file);
return super.visitFile(file, attrs);
}
@Override
public FileVisitResult visitFileFailed(Object file, IOException exc) throws IOException {
System.out.println("4 "+file);
return super.visitFileFailed(file, exc);
}
});
/** Output:
* 3 D:mnttest.txt
*/
}
}
删除目录树的方法实现依赖于 Files.walkFileTree()
,“walking” 目录树意味着遍历每个子目录和文件。Visitor 设计模式提供了一种标准机制来访问集合中的每个对象,然后你需要提供在每个对象上执行的操作。 此操作的定义取决于实现的 FileVisitor 的四个抽象方法,包括:
preVisitDirectory()
:在访问目录中条目之前在目录上运行。visitFile()
:运行目录中的每一个文件。visitFileFailed()
:调用无法访问的文件。postVisitDirectory()
:在访问目录中条目之后在目录上运行,包括所有的子目录。
文件系统
为了完整起见,我们需要一种方法查找文件系统相关的其他信息。在这里,我们使用静态的 FileSystems 工具类获取"默认"的文件系统,但你同样也可以在 Path 对象上调用 getFileSystem()
以获取创建该 Path 的文件系统。你可以获得给定 URI 的文件系统,还可以构建新的文件系统(对于支持它的操作系统)。
public class NewFileTest {
public static void main(String[] args) throws IOException {
Path p = Paths.get("D:","mnt","test.txt");
System.out.println(System.getProperty("os.name"));
System.out.println(p.getFileSystem());
/** Output:
* Windows 10
* sun.nio.fs.WindowsFileSystem@29453f44
*/
}
}
路径监听
通过 WatchService 可以设置一个进程对目录中的更改做出响应。
public class NewFileTest {
public static void main(String[] args) throws IOException, InterruptedException {
Path p = Paths.get("D:","mnt");
WatchService watchService = FileSystems.getDefault().newWatchService();
p.register(watchService, ENTRY_DELETE);
Executors.newSingleThreadScheduledExecutor()
.schedule(NewFileTest::del,
250, TimeUnit.MILLISECONDS);
WatchKey key = watchService.take();
for(WatchEvent evt : key.pollEvents()) {
System.out.println("evt.context(): " + evt.context() +
"nevt.count(): " + evt.count() +
"nevt.kind(): " + evt.kind());
System.exit(0);
}
}
static void del(){
System.out.println("delete");
}
}
一旦我们从 FileSystem 中得到了 WatchService 对象,我们将其注册到 mnt 路径以及我们感兴趣的项目的变量参数列表中,可以选择 ENTRY_CREATE,ENTRY_DELETE 或 ENTRY_MODIFY(其中创建和删除不属于修改)。接下来对 watcher.take()
的调用会在发生某些事情之前停止所有操作。Executors.newSingleThreadScheduledExecutor()
产生一个 ScheduledExecutorService 对象,然后调用 schedule()
方法传递所需函数的方法引用,并且设置在运行之前应该等待的时间。此时,watcher.take()
将等待并阻塞在这里。当目标事件发生时,会返回一个包含 WatchEvent 的 Watchkey 对象。展示的这三种方法是能对 WatchEvent 执行的全部操作。
文件查找
到目前为止,为了找到文件,我们一直使用相当粗糙的方法,在 path 上调用 toString()
,然后使用 string 操作查看结果。事实证明,java.nio.file
有更好的解决方案:通过在 FileSystem 对象上调用 getPathMatcher()
获得一个 PathMatcher,然后传入您感兴趣的模式。模式有两个选项:glob 和 regex。glob 比较简单,实际上功能非常强大,因此您可以使用 glob 解决许多问题。如果您的问题更复杂,可以使用 regex。
public class NewFileTest {
public static void main(String[] args) throws IOException{
Path p = Paths.get("D:","mnt");
PathMatcher matcher = FileSystems.getDefault()
.getPathMatcher("glob:**/*.{tmp,txt}");
Files.walk(p)
.filter(matcher::matches)
.forEach(System.out::println);
}
/** Output:
* D:mntdicthis.txt
* D:mnttest.txt
* D:mnttest1.txt
*/
}
在 matcher 中,glob 表达式开头的 **/ 表示“当前目录及所有子目录”,这在当你不仅仅要匹配当前目录下特定结尾的 Path 时非常有用。单 * 表示“任何东西”,然后是一个点,然后大括号表示一系列的可能性—我们正在寻找以 .tmp 或 .txt 结尾的东西。您可以在 getPathMatcher()
文档中找到更多详细信息。
文件读写
此时,我们可以对路径和目录做任何事情。 现在让我们看一下操纵文件本身的内容。如果一个文件很“小”,也就是说“它运行得足够快且占用内存小”,那么 java.nio.file.Files
类中的实用程序将帮助你轻松读写文本和二进制文件。Files.readAllLines()
一次读取整个文件(因此,“小”文件很有必要),乱码时或许你需要通过Charset来指定你的编码格式,产生一个List。
Files.write()
被重载以写入。byte 数组或任何 Iterable 对象(它也有 Charset 选项):
public class NewFileTest {
public static void main(String[] args) throws IOException, InterruptedException {
Path p = Paths.get("D:","mnt","test.txt");
Files.write(p,"hello,world!!!".getBytes());
Files.readAllLines(p,Charset.forName("utf-8")).forEach(System.out::print);
/** Output:
* hello world!!!!
*/
}
}
本章小结
Java 7 和 8 对于处理文件和目录的类库做了大量改进。当你真正熟练掌握时,这对你的代码编写有很大的提高。
最后
以上就是个性眼神为你收集整理的重拾Java基础知识:文件前言的全部内容,希望文章能够帮你解决重拾Java基础知识:文件前言所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复