文件
- 前言
- File类
- 文件和目录路径
- 选取路径部分片段
- 路径分析
- Paths的增减修改
- 目录
- 文件系统
- 路径监听
- 文件查找
- 文件读写
- 本章小结
前言
IO流,也就是输入输出流,从文件出发到文件结束,至始至终都离不开文件,所以IO流还得从文件File类讲起。
File类
专门对文件进行操作的类,只能对文件本身进行操作,不能对文件内容进行操作,主要用于文件和目录的创建、查找和删除等操作。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27public 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终止才进行删除 } }
执行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17path: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 对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31public 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()));//遍历路径下所有文件 } }
执行结果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22转换为绝对路径: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 对象可以非常容易地生成路径的某一部分:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23public 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。
路径分析
在文章最开始介绍了一些基本的用法,接着继续介绍:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28public 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()
标准化输出。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18public 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 工具类包含大部分我们需要的目录操作和文件操作方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public 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 的文件系统,还可以构建新的文件系统(对于支持它的操作系统)。
1
2
3
4
5
6
7
8
9
10
11
12public 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 可以设置一个进程对目录中的更改做出响应。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21public 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。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public 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 选项):
1
2
3
4
5
6
7
8
9
10
11public 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基础知识内容请搜索靠谱客的其他文章。
发表评论 取消回复