|
2 | 2 |
|
3 | 3 | <!-- File -->
|
4 | 4 | # 第十七章 文件
|
| 5 | +在丑陋的Java I/O编程方式诞生多年以后,Java终于简化了文件读写的基本操作。这种"困难方式"的全部细节都在[Appendix: I/O Streams]()。如果你读过这个部分,就会认同Java设计者毫不在意他们的使用者的体验这一观念。打开并读取文件对于大多数编程语言来是非常常用的,由于I/O糟糕的设计以至于 |
| 6 | +很少有人能够在不依赖其他参考代码的情况下完成打开文件的操作。 |
5 | 7 |
|
| 8 | +好像Java设计者终于意识到了Java使用者多年来的痛苦,在Java7中对此引入了巨大的改进。这些新元素被放在**java.nio.file**包下面,过去人们通常把**nio**中的**n**理解为**new**即新的**io**,现在更应该当成是**non-blocking**非阻塞**io**(**io**就是*input/output输入/输出*)。**java.nio.file**库终于将Java文件操作带到与其他编程语言相同的水平。最重要的是Java8新增的streams与文件结合使得文件操作编程变得更加优雅。我们将看一下文件操作的两个基本组件: |
| 9 | +1. 文件或者目录的路径; |
| 10 | +2. 文件本身。 |
6 | 11 |
|
7 | 12 | <!-- File and Directory Paths -->
|
8 | 13 | ## 文件和目录路径
|
| 14 | +### `Paths` |
| 15 | +一个**Path**对象表示一个文件或者目录的路径,是一个跨操作系统(OS)和文件系统的抽象,目的是在构造路径时不必关注底层操作系统,代码可以在不进行修改的情况下运行在不同的操作系统上。**java.nio.file.Paths**类包含一个重载方法**static get()**,该方法方法接受一系列**Strings**字符串或一个*统一资源标识符*(URI)作为参数,并且进行转换返回一个**Path**对象: |
| 16 | +```java |
| 17 | +// files/PathInfo.java |
| 18 | +import java.nio.file.*; |
| 19 | +import java.net.URI; |
| 20 | +import java.io.File; |
| 21 | +import java.io.IOException; |
9 | 22 |
|
| 23 | +public class PathInfo { |
| 24 | + static void show(String id, Object p) { |
| 25 | + System.out.println(id + ": " + p); |
| 26 | + } |
| 27 | + |
| 28 | + static void info(Path p) { |
| 29 | + show("toString", p); |
| 30 | + show("Exists", Files.exists(p)); |
| 31 | + show("RegularFile", Files.isRegularFile(p)); |
| 32 | + show("Directory", Files.isDirectory(p)); |
| 33 | + show("Absolute", p.isAbsolute()); |
| 34 | + show("FileName", p.getFileName()); |
| 35 | + show("Parent", p.getParent()); |
| 36 | + show("Root", p.getRoot()); |
| 37 | + System.out.println("******************"); |
| 38 | + } |
| 39 | + public static void main(String[] args) { |
| 40 | + System.out.println(System.getProperty("os.name")); |
| 41 | + info(Paths.get("C:", "path", "to", "nowhere", "NoFile.txt")); |
| 42 | + Path p = Paths.get("PathInfo.java"); |
| 43 | + info(p); |
| 44 | + Path ap = p.toAbsolutePath(); |
| 45 | + info(ap); |
| 46 | + info(ap.getParent()); |
| 47 | + try { |
| 48 | + info(p.toRealPath()); |
| 49 | + } catch(IOException e) { |
| 50 | + System.out.println(e); |
| 51 | + } |
| 52 | + URI u = p.toUri(); |
| 53 | + System.out.println("URI: " + u); |
| 54 | + Path puri = Paths.get(u); |
| 55 | + System.out.println(Files.exists(puri)); |
| 56 | + File f = ap.toFile(); // Don't be fooled |
| 57 | + } |
| 58 | +} |
10 | 59 |
|
11 |
| -<!-- Directories --> |
12 |
| -## 目录 |
| 60 | +/* 输出: |
| 61 | +Windows 10 |
| 62 | +toString: C:\path\to\nowhere\NoFile.txt |
| 63 | +Exists: false |
| 64 | +RegularFile: false |
| 65 | +Directory: false |
| 66 | +Absolute: true |
| 67 | +FileName: NoFile.txt |
| 68 | +Parent: C:\path\to\nowhere |
| 69 | +Root: C:\ |
| 70 | +****************** |
| 71 | +toString: PathInfo.java |
| 72 | +Exists: true |
| 73 | +RegularFile: true |
| 74 | +Directory: false |
| 75 | +Absolute: false |
| 76 | +FileName: PathInfo.java |
| 77 | +Parent: null |
| 78 | +Root: null |
| 79 | +****************** |
| 80 | +toString: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 81 | +ExtractedExamples\files\PathInfo.java |
| 82 | +Exists: true |
| 83 | +RegularFile: true |
| 84 | +Directory: false |
| 85 | +Absolute: true |
| 86 | +FileName: PathInfo.java |
| 87 | +Parent: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 88 | +ExtractedExamples\files |
| 89 | +Root: C:\ |
| 90 | +****************** |
| 91 | +toString: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 92 | +ExtractedExamples\files |
| 93 | +Exists: true |
| 94 | +RegularFile: false |
| 95 | +Directory: true |
| 96 | +Absolute: true |
| 97 | +FileName: files |
| 98 | +Parent: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 99 | +ExtractedExamples |
| 100 | +Root: C:\ |
| 101 | +****************** |
| 102 | +toString: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 103 | +ExtractedExamples\files\PathInfo.java |
| 104 | +Exists: true |
| 105 | +RegularFile: true |
| 106 | +Directory: false |
| 107 | +Absolute: true |
| 108 | +FileName: PathInfo.java |
| 109 | +Parent: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 110 | +ExtractedExamples\files |
| 111 | +Root: C:\ |
| 112 | +****************** |
| 113 | +URI: file:///C:/Users/Bruce/Documents/GitHub/onjava/ |
| 114 | +ExtractedExamples/files/PathInfo.java |
| 115 | +true |
| 116 | +*/ |
| 117 | +``` |
| 118 | + |
| 119 | +我已经在这一章第一个程序的**main()**方法添加了第一行用于展示操作系统的名称,因此你可以看到不同操作系统之间存在哪些差异。理想情况下,差别会相对较小,并且使用**/**或者**\\**路径分隔符进行分隔。你可以看到我运行在Windows 10上的程序输出。 |
| 120 | + |
| 121 | +当**toString()**方法生成完整形式的路径,你可以看到**getFileName()** 方法总是返回当前文件名。 |
| 122 | +通过使用**Files**工具类(我们接下类将会更多的使用它),可以测试一个文件是否存在,测试是否是一个"真正"的文件还是一个目录等等。"Nofile.txt"这个示例展示我们描述的文件可能并不在指定的位置;这样可以允许你创建一个新的路径。"PathInfo.java"存在于当前目录中,最初它只是没有路径的文件名,但它仍然被检测为"存在"。一旦我们将其转换为绝对路径,我们将会得到一个从"C:"盘(因为我们是在Windows机器下进行测试)开始的完整路径,现在它也拥有一个父路径。“真实”路径的定义在文档中有点模糊 |
| 123 | + 因为它取决于具体的文件系统。例如,如果文件名不区分大小写,即使路径由于大小写的缘故而不是完全相同,也可能得到肯定的匹配结果。 |
| 124 | + |
| 125 | +```java |
| 126 | +// files/PartsOfPaths.java |
| 127 | +import java.nio.file.*; |
| 128 | + |
| 129 | +public class PartsOfPaths { |
| 130 | + public static void main(String[] args) { |
| 131 | + System.out.println(System.getProperty("os.name")); |
| 132 | + Path p = Paths.get("PartsOfPaths.java").toAbsolutePath(); |
| 133 | + for(int i = 0; i < p.getNameCount(); i++) |
| 134 | + System.out.println(p.getName(i)); |
| 135 | + System.out.println("ends with '.java': " + |
| 136 | + p.endsWith(".java")); |
| 137 | + for(Path pp : p) { |
| 138 | + System.out.print(pp + ": "); |
| 139 | + System.out.print(p.startsWith(pp) + " : "); |
| 140 | + System.out.println(p.endsWith(pp)); |
| 141 | + } |
| 142 | + System.out.println("Starts with " + p.getRoot() + " " + p.startsWith(p.getRoot())); |
| 143 | + } |
| 144 | +} |
| 145 | + |
| 146 | +/* 输出: |
| 147 | +Windows 10 |
| 148 | +Users |
| 149 | +Bruce |
| 150 | +Documents |
| 151 | +GitHub |
| 152 | +on-java |
| 153 | +ExtractedExamples |
| 154 | +files |
| 155 | +PartsOfPaths.java |
| 156 | +ends with '.java': false |
| 157 | +Users: false : false |
| 158 | +Bruce: false : false |
| 159 | +Documents: false : false |
| 160 | +GitHub: false : false |
| 161 | +on-java: false : false |
| 162 | +ExtractedExamples: false : false |
| 163 | +files: false : false |
| 164 | +PartsOfPaths.java: false : true |
| 165 | +Starts with C:\ true |
| 166 | +*/ |
| 167 | +``` |
| 168 | + |
| 169 | +```java |
| 170 | +// files/PathAnalysis.java |
| 171 | +import java.nio.file.*; |
| 172 | +import java.io.IOException; |
13 | 173 |
|
| 174 | +public class PathAnalysis { |
| 175 | + static void say(String id, Object result) { |
| 176 | + System.out.print(id + ": "); |
| 177 | + System.out.println(result); |
| 178 | + } |
| 179 | + |
| 180 | + public static void main(String[] args) throws IOException { |
| 181 | + System.out.println(System.getProperty("os.name")); |
| 182 | + Path p = Paths.get("PathAnalysis.java").toAbsolutePath(); |
| 183 | + say("Exists", Files.exists(p)); |
| 184 | + say("Directory", Files.isDirectory(p)); |
| 185 | + say("Executable", Files.isExecutable(p)); |
| 186 | + say("Readable", Files.isReadable(p)); |
| 187 | + say("RegularFile", Files.isRegularFile(p)); |
| 188 | + say("Writable", Files.isWritable(p)); |
| 189 | + say("notExists", Files.notExists(p)); |
| 190 | + say("Hidden", Files.isHidden(p)); |
| 191 | + say("size", Files.size(p)); |
| 192 | + say("FileStore", Files.getFileStore(p)); |
| 193 | + say("LastModified: ", Files.getLastModifiedTime(p)); |
| 194 | + say("Owner", Files.getOwner(p)); |
| 195 | + say("ContentType", Files.probeContentType(p)); |
| 196 | + say("SymbolicLink", Files.isSymbolicLink(p)); |
| 197 | + if(Files.isSymbolicLink(p)) |
| 198 | + say("SymbolicLink", Files.readSymbolicLink(p)); |
| 199 | + if(FileSystems.getDefault().supportedFileAttributeViews().contains("posix")) |
| 200 | + say("PosixFilePermissions", |
| 201 | + Files.getPosixFilePermissions(p)); |
| 202 | + } |
| 203 | +} |
14 | 204 |
|
| 205 | +/* 输出: |
| 206 | +Windows 10 |
| 207 | +Exists: true |
| 208 | +Directory: false |
| 209 | +Executable: true |
| 210 | +Readable: true |
| 211 | +RegularFile: true |
| 212 | +Writable: true |
| 213 | +notExists: false |
| 214 | +Hidden: false |
| 215 | +size: 1631 |
| 216 | +FileStore: SSD (C:) |
| 217 | +LastModified: : 2017-05-09T12:07:00.428366Z |
| 218 | +Owner: MINDVIEWTOSHIBA\Bruce (User) |
| 219 | +ContentType: null |
| 220 | +SymbolicLink: false |
| 221 | +*/ |
| 222 | +``` |
| 223 | + |
| 224 | +```java |
| 225 | +// files/AddAndSubtractPaths.java |
| 226 | +import java.nio.file.*; |
| 227 | +import java.io.IOException; |
| 228 | + |
| 229 | +public class AddAndSubtractPaths { |
| 230 | + static Path base = Paths.get("..", "..", "..").toAbsolutePath().normalize(); |
| 231 | + |
| 232 | + static void show(int id, Path result) { |
| 233 | + if(result.isAbsolute()) |
| 234 | + System.out.println("(" + id + ")r " + base.relativize(result)); |
| 235 | + else |
| 236 | + System.out.println("(" + id + ") " + result); |
| 237 | + try { |
| 238 | + System.out.println("RealPath: " + result.toRealPath()); |
| 239 | + } catch(IOException e) { |
| 240 | + System.out.println(e); |
| 241 | + } |
| 242 | + } |
| 243 | + |
| 244 | + public static void main(String[] args) { |
| 245 | + System.out.println(System.getProperty("os.name")); |
| 246 | + System.out.println(base); |
| 247 | + Path p = Paths.get("AddAndSubtractPaths.java").toAbsolutePath(); |
| 248 | + show(1, p); |
| 249 | + Path convoluted = p.getParent().getParent() |
| 250 | + .resolve("strings").resolve("..") |
| 251 | + .resolve(p.getParent().getFileName()); |
| 252 | + show(2, convoluted); |
| 253 | + show(3, convoluted.normalize()); |
| 254 | + Path p2 = Paths.get("..", ".."); |
| 255 | + show(4, p2); |
| 256 | + show(5, p2.normalize()); |
| 257 | + show(6, p2.toAbsolutePath().normalize()); |
| 258 | + Path p3 = Paths.get(".").toAbsolutePath(); |
| 259 | + Path p4 = p3.resolve(p2); |
| 260 | + show(7, p4); |
| 261 | + show(8, p4.normalize()); |
| 262 | + Path p5 = Paths.get("").toAbsolutePath(); |
| 263 | + show(9, p5); |
| 264 | + show(10, p5.resolveSibling("strings")); |
| 265 | + show(11, Paths.get("nonexistent")); |
| 266 | + } |
| 267 | +} |
| 268 | + |
| 269 | +/* 输出: |
| 270 | +Windows 10 |
| 271 | +C:\Users\Bruce\Documents\GitHub |
| 272 | +(1)r onjava\ |
| 273 | +ExtractedExamples\files\AddAndSubtractPaths.java |
| 274 | +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 275 | +ExtractedExamples\files\AddAndSubtractPaths.java |
| 276 | +(2)r on-java\ExtractedExamples\strings\..\files |
| 277 | +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 278 | +ExtractedExamples\files |
| 279 | +(3)r on-java\ExtractedExamples\files |
| 280 | +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 281 | +ExtractedExamples\files |
| 282 | +(4) ..\.. |
| 283 | +RealPath: C:\Users\Bruce\Documents\GitHub\on-java |
| 284 | +(5) ..\.. |
| 285 | +RealPath: C:\Users\Bruce\Documents\GitHub\on-java |
| 286 | +(6)r on-java |
| 287 | +RealPath: C:\Users\Bruce\Documents\GitHub\on-java |
| 288 | +(7)r on-java\ExtractedExamples\files\.\..\.. |
| 289 | +RealPath: C:\Users\Bruce\Documents\GitHub\on-java |
| 290 | +(8)r on-java |
| 291 | +RealPath: C:\Users\Bruce\Documents\GitHub\on-java |
| 292 | +(9)r on-java\ExtractedExamples\files |
| 293 | +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 294 | +ExtractedExamples\files |
| 295 | +(10)r on-java\ExtractedExamples\strings |
| 296 | +RealPath: C:\Users\Bruce\Documents\GitHub\onjava\ |
| 297 | +ExtractedExamples\strings |
| 298 | +(11) nonexistent |
| 299 | +java.nio.file.NoSuchFileException: |
| 300 | +C:\Users\Bruce\Documents\GitHub\onjava\ |
| 301 | +ExtractedExamples\files\nonexistent |
| 302 | +*/ |
| 303 | +``` |
15 | 304 | <!-- File Systems -->
|
| 305 | + |
| 306 | +<!-- Directories --> |
| 307 | +## 目录 |
| 308 | + |
16 | 309 | ## 文件系统
|
17 | 310 |
|
18 | 311 |
|
|
0 commit comments