1 /* 2 * Copyright (C) 2007 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.google.common.io; 18 19 import static com.google.common.base.Preconditions.checkArgument; 20 import static com.google.common.base.Preconditions.checkNotNull; 21 import static com.google.common.io.FileWriteMode.APPEND; 22 23 import com.google.common.annotations.Beta; 24 import com.google.common.base.Charsets; 25 import com.google.common.base.Joiner; 26 import com.google.common.base.Predicate; 27 import com.google.common.base.Splitter; 28 import com.google.common.collect.ImmutableSet; 29 import com.google.common.collect.Lists; 30 import com.google.common.collect.TreeTraverser; 31 import com.google.common.hash.HashCode; 32 import com.google.common.hash.HashFunction; 33 34 import java.io.BufferedReader; 35 import java.io.BufferedWriter; 36 import java.io.Closeable; 37 import java.io.File; 38 import java.io.FileInputStream; 39 import java.io.FileNotFoundException; 40 import java.io.FileOutputStream; 41 import java.io.IOException; 42 import java.io.InputStream; 43 import java.io.InputStreamReader; 44 import java.io.OutputStream; 45 import java.io.OutputStreamWriter; 46 import java.io.RandomAccessFile; 47 import java.nio.MappedByteBuffer; 48 import java.nio.channels.FileChannel; 49 import java.nio.channels.FileChannel.MapMode; 50 import java.nio.charset.Charset; 51 import java.util.ArrayList; 52 import java.util.Arrays; 53 import java.util.Collections; 54 import java.util.List; 55 import java.util.zip.Checksum; 56 57 /** 58 * Provides utility methods for working with files. 59 * 60 * <p>All method parameters must be non-null unless documented otherwise. 61 * 62 * @author Chris Nokleberg 63 * @author Colin Decker 64 * @since 1.0 65 */ 66 @Beta 67 public final class Files { 68 69 /** Maximum loop count when creating temp directories. */ 70 private static final int TEMP_DIR_ATTEMPTS = 10000; 71 Files()72 private Files() {} 73 74 /** 75 * Returns a buffered reader that reads from a file using the given 76 * character set. 77 * 78 * @param file the file to read from 79 * @param charset the charset used to decode the input stream; see {@link 80 * Charsets} for helpful predefined constants 81 * @return the buffered reader 82 */ newReader(File file, Charset charset)83 public static BufferedReader newReader(File file, Charset charset) 84 throws FileNotFoundException { 85 checkNotNull(file); 86 checkNotNull(charset); 87 return new BufferedReader( 88 new InputStreamReader(new FileInputStream(file), charset)); 89 } 90 91 /** 92 * Returns a buffered writer that writes to a file using the given 93 * character set. 94 * 95 * @param file the file to write to 96 * @param charset the charset used to encode the output stream; see {@link 97 * Charsets} for helpful predefined constants 98 * @return the buffered writer 99 */ newWriter(File file, Charset charset)100 public static BufferedWriter newWriter(File file, Charset charset) 101 throws FileNotFoundException { 102 checkNotNull(file); 103 checkNotNull(charset); 104 return new BufferedWriter( 105 new OutputStreamWriter(new FileOutputStream(file), charset)); 106 } 107 108 /** 109 * Returns a new {@link ByteSource} for reading bytes from the given file. 110 * 111 * @since 14.0 112 */ asByteSource(File file)113 public static ByteSource asByteSource(File file) { 114 return new FileByteSource(file); 115 } 116 117 private static final class FileByteSource extends ByteSource { 118 119 private final File file; 120 FileByteSource(File file)121 private FileByteSource(File file) { 122 this.file = checkNotNull(file); 123 } 124 125 @Override openStream()126 public FileInputStream openStream() throws IOException { 127 return new FileInputStream(file); 128 } 129 130 @Override size()131 public long size() throws IOException { 132 if (!file.isFile()) { 133 throw new FileNotFoundException(file.toString()); 134 } 135 return file.length(); 136 } 137 138 @Override read()139 public byte[] read() throws IOException { 140 Closer closer = Closer.create(); 141 try { 142 FileInputStream in = closer.register(openStream()); 143 return readFile(in, in.getChannel().size()); 144 } catch (Throwable e) { 145 throw closer.rethrow(e); 146 } finally { 147 closer.close(); 148 } 149 } 150 151 @Override toString()152 public String toString() { 153 return "Files.asByteSource(" + file + ")"; 154 } 155 } 156 157 /** 158 * Reads a file of the given expected size from the given input stream, if 159 * it will fit into a byte array. This method handles the case where the file 160 * size changes between when the size is read and when the contents are read 161 * from the stream. 162 */ readFile( InputStream in, long expectedSize)163 static byte[] readFile( 164 InputStream in, long expectedSize) throws IOException { 165 if (expectedSize > Integer.MAX_VALUE) { 166 throw new OutOfMemoryError("file is too large to fit in a byte array: " 167 + expectedSize + " bytes"); 168 } 169 170 // some special files may return size 0 but have content, so read 171 // the file normally in that case 172 return expectedSize == 0 173 ? ByteStreams.toByteArray(in) 174 : ByteStreams.toByteArray(in, (int) expectedSize); 175 } 176 177 /** 178 * Returns a new {@link ByteSink} for writing bytes to the given file. The 179 * given {@code modes} control how the file is opened for writing. When no 180 * mode is provided, the file will be truncated before writing. When the 181 * {@link FileWriteMode#APPEND APPEND} mode is provided, writes will 182 * append to the end of the file without truncating it. 183 * 184 * @since 14.0 185 */ asByteSink(File file, FileWriteMode... modes)186 public static ByteSink asByteSink(File file, FileWriteMode... modes) { 187 return new FileByteSink(file, modes); 188 } 189 190 private static final class FileByteSink extends ByteSink { 191 192 private final File file; 193 private final ImmutableSet<FileWriteMode> modes; 194 FileByteSink(File file, FileWriteMode... modes)195 private FileByteSink(File file, FileWriteMode... modes) { 196 this.file = checkNotNull(file); 197 this.modes = ImmutableSet.copyOf(modes); 198 } 199 200 @Override openStream()201 public FileOutputStream openStream() throws IOException { 202 return new FileOutputStream(file, modes.contains(APPEND)); 203 } 204 205 @Override toString()206 public String toString() { 207 return "Files.asByteSink(" + file + ", " + modes + ")"; 208 } 209 } 210 211 /** 212 * Returns a new {@link CharSource} for reading character data from the given 213 * file using the given character set. 214 * 215 * @since 14.0 216 */ asCharSource(File file, Charset charset)217 public static CharSource asCharSource(File file, Charset charset) { 218 return asByteSource(file).asCharSource(charset); 219 } 220 221 /** 222 * Returns a new {@link CharSink} for writing character data to the given 223 * file using the given character set. The given {@code modes} control how 224 * the file is opened for writing. When no mode is provided, the file 225 * will be truncated before writing. When the 226 * {@link FileWriteMode#APPEND APPEND} mode is provided, writes will 227 * append to the end of the file without truncating it. 228 * 229 * @since 14.0 230 */ asCharSink(File file, Charset charset, FileWriteMode... modes)231 public static CharSink asCharSink(File file, Charset charset, 232 FileWriteMode... modes) { 233 return asByteSink(file, modes).asCharSink(charset); 234 } 235 236 /** 237 * Returns a factory that will supply instances of {@link FileInputStream} 238 * that read from a file. 239 * 240 * @param file the file to read from 241 * @return the factory 242 * @deprecated Use {@link #asByteSource(File)}. This method is scheduled for 243 * removal in Guava 18.0. 244 */ 245 @Deprecated newInputStreamSupplier( final File file)246 public static InputSupplier<FileInputStream> newInputStreamSupplier( 247 final File file) { 248 return ByteStreams.asInputSupplier(asByteSource(file)); 249 } 250 251 /** 252 * Returns a factory that will supply instances of {@link FileOutputStream} 253 * that write to a file. 254 * 255 * @param file the file to write to 256 * @return the factory 257 * @deprecated Use {@link #asByteSink(File)}. This method is scheduled for 258 * removal in Guava 18.0. 259 */ 260 @Deprecated newOutputStreamSupplier( File file)261 public static OutputSupplier<FileOutputStream> newOutputStreamSupplier( 262 File file) { 263 return newOutputStreamSupplier(file, false); 264 } 265 266 /** 267 * Returns a factory that will supply instances of {@link FileOutputStream} 268 * that write to or append to a file. 269 * 270 * @param file the file to write to 271 * @param append if true, the encoded characters will be appended to the file; 272 * otherwise the file is overwritten 273 * @return the factory 274 * @deprecated Use {@link #asByteSink(File, FileWriteMode...)}, passing 275 * {@link FileWriteMode#APPEND} for append. This method is scheduled for 276 * removal in Guava 18.0. 277 */ 278 @Deprecated newOutputStreamSupplier( final File file, final boolean append)279 public static OutputSupplier<FileOutputStream> newOutputStreamSupplier( 280 final File file, final boolean append) { 281 return ByteStreams.asOutputSupplier(asByteSink(file, modes(append))); 282 } 283 modes(boolean append)284 private static FileWriteMode[] modes(boolean append) { 285 return append 286 ? new FileWriteMode[]{ FileWriteMode.APPEND } 287 : new FileWriteMode[0]; 288 } 289 290 /** 291 * Returns a factory that will supply instances of 292 * {@link InputStreamReader} that read a file using the given character set. 293 * 294 * @param file the file to read from 295 * @param charset the charset used to decode the input stream; see {@link 296 * Charsets} for helpful predefined constants 297 * @return the factory 298 * @deprecated Use {@link #asCharSource(File, Charset)}. This method is 299 * scheduled for removal in Guava 18.0. 300 */ 301 @Deprecated newReaderSupplier(File file, Charset charset)302 public static InputSupplier<InputStreamReader> newReaderSupplier(File file, 303 Charset charset) { 304 return CharStreams.asInputSupplier(asCharSource(file, charset)); 305 } 306 307 /** 308 * Returns a factory that will supply instances of {@link OutputStreamWriter} 309 * that write to a file using the given character set. 310 * 311 * @param file the file to write to 312 * @param charset the charset used to encode the output stream; see {@link 313 * Charsets} for helpful predefined constants 314 * @return the factory 315 * @deprecated Use {@link #asCharSink(File, Charset)}. This method is 316 * scheduled for removal in Guava 18.0. 317 */ 318 @Deprecated newWriterSupplier(File file, Charset charset)319 public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file, 320 Charset charset) { 321 return newWriterSupplier(file, charset, false); 322 } 323 324 /** 325 * Returns a factory that will supply instances of {@link OutputStreamWriter} 326 * that write to or append to a file using the given character set. 327 * 328 * @param file the file to write to 329 * @param charset the charset used to encode the output stream; see {@link 330 * Charsets} for helpful predefined constants 331 * @param append if true, the encoded characters will be appended to the file; 332 * otherwise the file is overwritten 333 * @return the factory 334 * @deprecated Use {@link #asCharSink(File, Charset, FileWriteMode...)}, 335 * passing {@link FileWriteMode#APPEND} for append. This method is 336 * scheduled for removal in Guava 18.0. 337 */ 338 @Deprecated newWriterSupplier(File file, Charset charset, boolean append)339 public static OutputSupplier<OutputStreamWriter> newWriterSupplier(File file, 340 Charset charset, boolean append) { 341 return CharStreams.asOutputSupplier(asCharSink(file, charset, modes(append))); 342 } 343 344 /** 345 * Reads all bytes from a file into a byte array. 346 * 347 * @param file the file to read from 348 * @return a byte array containing all the bytes from file 349 * @throws IllegalArgumentException if the file is bigger than the largest 350 * possible byte array (2^31 - 1) 351 * @throws IOException if an I/O error occurs 352 */ toByteArray(File file)353 public static byte[] toByteArray(File file) throws IOException { 354 return asByteSource(file).read(); 355 } 356 357 /** 358 * Reads all characters from a file into a {@link String}, using the given 359 * character set. 360 * 361 * @param file the file to read from 362 * @param charset the charset used to decode the input stream; see {@link 363 * Charsets} for helpful predefined constants 364 * @return a string containing all the characters from the file 365 * @throws IOException if an I/O error occurs 366 */ toString(File file, Charset charset)367 public static String toString(File file, Charset charset) throws IOException { 368 return asCharSource(file, charset).read(); 369 } 370 371 /** 372 * Copies to a file all bytes from an {@link InputStream} supplied by a 373 * factory. 374 * 375 * @param from the input factory 376 * @param to the destination file 377 * @throws IOException if an I/O error occurs 378 * @deprecated Use {@code from.copyTo(Files.asByteSink(to))} after changing 379 * {@code from} to a {@code ByteSource} if necessary. This method is 380 * scheduled to be removed in Guava 18.0. 381 */ 382 @Deprecated copy(InputSupplier<? extends InputStream> from, File to)383 public static void copy(InputSupplier<? extends InputStream> from, File to) 384 throws IOException { 385 ByteStreams.asByteSource(from).copyTo(asByteSink(to)); 386 } 387 388 /** 389 * Overwrites a file with the contents of a byte array. 390 * 391 * @param from the bytes to write 392 * @param to the destination file 393 * @throws IOException if an I/O error occurs 394 */ write(byte[] from, File to)395 public static void write(byte[] from, File to) throws IOException { 396 asByteSink(to).write(from); 397 } 398 399 /** 400 * Copies all bytes from a file to an {@link OutputStream} supplied by 401 * a factory. 402 * 403 * @param from the source file 404 * @param to the output factory 405 * @throws IOException if an I/O error occurs 406 * @deprecated Use {@code Files.asByteSource(from).copyTo(to)} after changing 407 * {@code to} to a {@code ByteSink} if necessary. This method is 408 * scheduled to be removed in Guava 18.0. 409 */ 410 @Deprecated copy(File from, OutputSupplier<? extends OutputStream> to)411 public static void copy(File from, OutputSupplier<? extends OutputStream> to) 412 throws IOException { 413 asByteSource(from).copyTo(ByteStreams.asByteSink(to)); 414 } 415 416 /** 417 * Copies all bytes from a file to an output stream. 418 * 419 * @param from the source file 420 * @param to the output stream 421 * @throws IOException if an I/O error occurs 422 */ copy(File from, OutputStream to)423 public static void copy(File from, OutputStream to) throws IOException { 424 asByteSource(from).copyTo(to); 425 } 426 427 /** 428 * Copies all the bytes from one file to another. 429 * 430 * <p><b>Warning:</b> If {@code to} represents an existing file, that file 431 * will be overwritten with the contents of {@code from}. If {@code to} and 432 * {@code from} refer to the <i>same</i> file, the contents of that file 433 * will be deleted. 434 * 435 * @param from the source file 436 * @param to the destination file 437 * @throws IOException if an I/O error occurs 438 * @throws IllegalArgumentException if {@code from.equals(to)} 439 */ copy(File from, File to)440 public static void copy(File from, File to) throws IOException { 441 checkArgument(!from.equals(to), 442 "Source %s and destination %s must be different", from, to); 443 asByteSource(from).copyTo(asByteSink(to)); 444 } 445 446 /** 447 * Copies to a file all characters from a {@link Readable} and 448 * {@link Closeable} object supplied by a factory, using the given 449 * character set. 450 * 451 * @param from the readable supplier 452 * @param to the destination file 453 * @param charset the charset used to encode the output stream; see {@link 454 * Charsets} for helpful predefined constants 455 * @throws IOException if an I/O error occurs 456 * @deprecated Use {@code from.copyTo(Files.asCharSink(to, charset))} after 457 * changing {@code from} to a {@code CharSource} if necessary. This 458 * method is scheduled to be removed in Guava 18.0. 459 */ 460 @Deprecated copy( InputSupplier<R> from, File to, Charset charset)461 public static <R extends Readable & Closeable> void copy( 462 InputSupplier<R> from, File to, Charset charset) throws IOException { 463 CharStreams.asCharSource(from).copyTo(asCharSink(to, charset)); 464 } 465 466 /** 467 * Writes a character sequence (such as a string) to a file using the given 468 * character set. 469 * 470 * @param from the character sequence to write 471 * @param to the destination file 472 * @param charset the charset used to encode the output stream; see {@link 473 * Charsets} for helpful predefined constants 474 * @throws IOException if an I/O error occurs 475 */ write(CharSequence from, File to, Charset charset)476 public static void write(CharSequence from, File to, Charset charset) 477 throws IOException { 478 asCharSink(to, charset).write(from); 479 } 480 481 /** 482 * Appends a character sequence (such as a string) to a file using the given 483 * character set. 484 * 485 * @param from the character sequence to append 486 * @param to the destination file 487 * @param charset the charset used to encode the output stream; see {@link 488 * Charsets} for helpful predefined constants 489 * @throws IOException if an I/O error occurs 490 */ append(CharSequence from, File to, Charset charset)491 public static void append(CharSequence from, File to, Charset charset) 492 throws IOException { 493 write(from, to, charset, true); 494 } 495 496 /** 497 * Private helper method. Writes a character sequence to a file, 498 * optionally appending. 499 * 500 * @param from the character sequence to append 501 * @param to the destination file 502 * @param charset the charset used to encode the output stream; see {@link 503 * Charsets} for helpful predefined constants 504 * @param append true to append, false to overwrite 505 * @throws IOException if an I/O error occurs 506 */ write(CharSequence from, File to, Charset charset, boolean append)507 private static void write(CharSequence from, File to, Charset charset, 508 boolean append) throws IOException { 509 asCharSink(to, charset, modes(append)).write(from); 510 } 511 512 /** 513 * Copies all characters from a file to a {@link Appendable} & 514 * {@link Closeable} object supplied by a factory, using the given 515 * character set. 516 * 517 * @param from the source file 518 * @param charset the charset used to decode the input stream; see {@link 519 * Charsets} for helpful predefined constants 520 * @param to the appendable supplier 521 * @throws IOException if an I/O error occurs 522 * @deprecated Use {@code Files.asCharSource(from, charset).copyTo(to)} after 523 * changing {@code to} to a {@code CharSink} if necessary. This method is 524 * scheduled to be removed in Guava 18.0. 525 */ 526 @Deprecated copy(File from, Charset charset, OutputSupplier<W> to)527 public static <W extends Appendable & Closeable> void copy(File from, 528 Charset charset, OutputSupplier<W> to) throws IOException { 529 asCharSource(from, charset).copyTo(CharStreams.asCharSink(to)); 530 } 531 532 /** 533 * Copies all characters from a file to an appendable object, 534 * using the given character set. 535 * 536 * @param from the source file 537 * @param charset the charset used to decode the input stream; see {@link 538 * Charsets} for helpful predefined constants 539 * @param to the appendable object 540 * @throws IOException if an I/O error occurs 541 */ copy(File from, Charset charset, Appendable to)542 public static void copy(File from, Charset charset, Appendable to) 543 throws IOException { 544 asCharSource(from, charset).copyTo(to); 545 } 546 547 /** 548 * Returns true if the files contains the same bytes. 549 * 550 * @throws IOException if an I/O error occurs 551 */ equal(File file1, File file2)552 public static boolean equal(File file1, File file2) throws IOException { 553 checkNotNull(file1); 554 checkNotNull(file2); 555 if (file1 == file2 || file1.equals(file2)) { 556 return true; 557 } 558 559 /* 560 * Some operating systems may return zero as the length for files 561 * denoting system-dependent entities such as devices or pipes, in 562 * which case we must fall back on comparing the bytes directly. 563 */ 564 long len1 = file1.length(); 565 long len2 = file2.length(); 566 if (len1 != 0 && len2 != 0 && len1 != len2) { 567 return false; 568 } 569 return asByteSource(file1).contentEquals(asByteSource(file2)); 570 } 571 572 /** 573 * Atomically creates a new directory somewhere beneath the system's 574 * temporary directory (as defined by the {@code java.io.tmpdir} system 575 * property), and returns its name. 576 * 577 * <p>Use this method instead of {@link File#createTempFile(String, String)} 578 * when you wish to create a directory, not a regular file. A common pitfall 579 * is to call {@code createTempFile}, delete the file and create a 580 * directory in its place, but this leads a race condition which can be 581 * exploited to create security vulnerabilities, especially when executable 582 * files are to be written into the directory. 583 * 584 * <p>This method assumes that the temporary volume is writable, has free 585 * inodes and free blocks, and that it will not be called thousands of times 586 * per second. 587 * 588 * @return the newly-created directory 589 * @throws IllegalStateException if the directory could not be created 590 */ createTempDir()591 public static File createTempDir() { 592 File baseDir = new File(System.getProperty("java.io.tmpdir")); 593 String baseName = System.currentTimeMillis() + "-"; 594 595 for (int counter = 0; counter < TEMP_DIR_ATTEMPTS; counter++) { 596 File tempDir = new File(baseDir, baseName + counter); 597 if (tempDir.mkdir()) { 598 return tempDir; 599 } 600 } 601 throw new IllegalStateException("Failed to create directory within " 602 + TEMP_DIR_ATTEMPTS + " attempts (tried " 603 + baseName + "0 to " + baseName + (TEMP_DIR_ATTEMPTS - 1) + ')'); 604 } 605 606 /** 607 * Creates an empty file or updates the last updated timestamp on the 608 * same as the unix command of the same name. 609 * 610 * @param file the file to create or update 611 * @throws IOException if an I/O error occurs 612 */ touch(File file)613 public static void touch(File file) throws IOException { 614 checkNotNull(file); 615 if (!file.createNewFile() 616 && !file.setLastModified(System.currentTimeMillis())) { 617 throw new IOException("Unable to update modification time of " + file); 618 } 619 } 620 621 /** 622 * Creates any necessary but nonexistent parent directories of the specified 623 * file. Note that if this operation fails it may have succeeded in creating 624 * some (but not all) of the necessary parent directories. 625 * 626 * @throws IOException if an I/O error occurs, or if any necessary but 627 * nonexistent parent directories of the specified file could not be 628 * created. 629 * @since 4.0 630 */ createParentDirs(File file)631 public static void createParentDirs(File file) throws IOException { 632 checkNotNull(file); 633 File parent = file.getCanonicalFile().getParentFile(); 634 if (parent == null) { 635 /* 636 * The given directory is a filesystem root. All zero of its ancestors 637 * exist. This doesn't mean that the root itself exists -- consider x:\ on 638 * a Windows machine without such a drive -- or even that the caller can 639 * create it, but this method makes no such guarantees even for non-root 640 * files. 641 */ 642 return; 643 } 644 parent.mkdirs(); 645 if (!parent.isDirectory()) { 646 throw new IOException("Unable to create parent directories of " + file); 647 } 648 } 649 650 /** 651 * Moves a file from one path to another. This method can rename a file 652 * and/or move it to a different directory. In either case {@code to} must 653 * be the target path for the file itself; not just the new name for the 654 * file or the path to the new parent directory. 655 * 656 * @param from the source file 657 * @param to the destination file 658 * @throws IOException if an I/O error occurs 659 * @throws IllegalArgumentException if {@code from.equals(to)} 660 */ move(File from, File to)661 public static void move(File from, File to) throws IOException { 662 checkNotNull(from); 663 checkNotNull(to); 664 checkArgument(!from.equals(to), 665 "Source %s and destination %s must be different", from, to); 666 667 if (!from.renameTo(to)) { 668 copy(from, to); 669 if (!from.delete()) { 670 if (!to.delete()) { 671 throw new IOException("Unable to delete " + to); 672 } 673 throw new IOException("Unable to delete " + from); 674 } 675 } 676 } 677 678 /** 679 * Reads the first line from a file. The line does not include 680 * line-termination characters, but does include other leading and 681 * trailing whitespace. 682 * 683 * @param file the file to read from 684 * @param charset the charset used to decode the input stream; see {@link 685 * Charsets} for helpful predefined constants 686 * @return the first line, or null if the file is empty 687 * @throws IOException if an I/O error occurs 688 */ readFirstLine(File file, Charset charset)689 public static String readFirstLine(File file, Charset charset) 690 throws IOException { 691 return asCharSource(file, charset).readFirstLine(); 692 } 693 694 /** 695 * Reads all of the lines from a file. The lines do not include 696 * line-termination characters, but do include other leading and 697 * trailing whitespace. 698 * 699 * <p>This method returns a mutable {@code List}. For an 700 * {@code ImmutableList}, use 701 * {@code Files.asCharSource(file, charset).readLines()}. 702 * 703 * @param file the file to read from 704 * @param charset the charset used to decode the input stream; see {@link 705 * Charsets} for helpful predefined constants 706 * @return a mutable {@link List} containing all the lines 707 * @throws IOException if an I/O error occurs 708 */ readLines(File file, Charset charset)709 public static List<String> readLines(File file, Charset charset) 710 throws IOException { 711 // don't use asCharSource(file, charset).readLines() because that returns 712 // an immutable list, which would change the behavior of this method 713 return readLines(file, charset, new LineProcessor<List<String>>() { 714 final List<String> result = Lists.newArrayList(); 715 716 @Override 717 public boolean processLine(String line) { 718 result.add(line); 719 return true; 720 } 721 722 @Override 723 public List<String> getResult() { 724 return result; 725 } 726 }); 727 } 728 729 /** 730 * Streams lines from a {@link File}, stopping when our callback returns 731 * false, or we have read all of the lines. 732 * 733 * @param file the file to read from 734 * @param charset the charset used to decode the input stream; see {@link 735 * Charsets} for helpful predefined constants 736 * @param callback the {@link LineProcessor} to use to handle the lines 737 * @return the output of processing the lines 738 * @throws IOException if an I/O error occurs 739 */ readLines(File file, Charset charset, LineProcessor<T> callback)740 public static <T> T readLines(File file, Charset charset, 741 LineProcessor<T> callback) throws IOException { 742 return CharStreams.readLines(newReaderSupplier(file, charset), callback); 743 } 744 745 /** 746 * Process the bytes of a file. 747 * 748 * <p>(If this seems too complicated, maybe you're looking for 749 * {@link #toByteArray}.) 750 * 751 * @param file the file to read 752 * @param processor the object to which the bytes of the file are passed. 753 * @return the result of the byte processor 754 * @throws IOException if an I/O error occurs 755 */ readBytes(File file, ByteProcessor<T> processor)756 public static <T> T readBytes(File file, ByteProcessor<T> processor) 757 throws IOException { 758 return ByteStreams.readBytes(newInputStreamSupplier(file), processor); 759 } 760 761 /** 762 * Computes and returns the checksum value for a file. 763 * The checksum object is reset when this method returns successfully. 764 * 765 * @param file the file to read 766 * @param checksum the checksum object 767 * @return the result of {@link Checksum#getValue} after updating the 768 * checksum object with all of the bytes in the file 769 * @throws IOException if an I/O error occurs 770 * @deprecated Use {@code hash} with the {@code Hashing.crc32()} or 771 * {@code Hashing.adler32()} hash functions. This method is scheduled 772 * to be removed in Guava 15.0. 773 */ 774 @Deprecated getChecksum(File file, Checksum checksum)775 public static long getChecksum(File file, Checksum checksum) 776 throws IOException { 777 return ByteStreams.getChecksum(newInputStreamSupplier(file), checksum); 778 } 779 780 /** 781 * Computes the hash code of the {@code file} using {@code hashFunction}. 782 * 783 * @param file the file to read 784 * @param hashFunction the hash function to use to hash the data 785 * @return the {@link HashCode} of all of the bytes in the file 786 * @throws IOException if an I/O error occurs 787 * @since 12.0 788 */ hash(File file, HashFunction hashFunction)789 public static HashCode hash(File file, HashFunction hashFunction) 790 throws IOException { 791 return asByteSource(file).hash(hashFunction); 792 } 793 794 /** 795 * Fully maps a file read-only in to memory as per 796 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)}. 797 * 798 * <p>Files are mapped from offset 0 to its length. 799 * 800 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes. 801 * 802 * @param file the file to map 803 * @return a read-only buffer reflecting {@code file} 804 * @throws FileNotFoundException if the {@code file} does not exist 805 * @throws IOException if an I/O error occurs 806 * 807 * @see FileChannel#map(MapMode, long, long) 808 * @since 2.0 809 */ map(File file)810 public static MappedByteBuffer map(File file) throws IOException { 811 checkNotNull(file); 812 return map(file, MapMode.READ_ONLY); 813 } 814 815 /** 816 * Fully maps a file in to memory as per 817 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)} 818 * using the requested {@link MapMode}. 819 * 820 * <p>Files are mapped from offset 0 to its length. 821 * 822 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes. 823 * 824 * @param file the file to map 825 * @param mode the mode to use when mapping {@code file} 826 * @return a buffer reflecting {@code file} 827 * @throws FileNotFoundException if the {@code file} does not exist 828 * @throws IOException if an I/O error occurs 829 * 830 * @see FileChannel#map(MapMode, long, long) 831 * @since 2.0 832 */ map(File file, MapMode mode)833 public static MappedByteBuffer map(File file, MapMode mode) 834 throws IOException { 835 checkNotNull(file); 836 checkNotNull(mode); 837 if (!file.exists()) { 838 throw new FileNotFoundException(file.toString()); 839 } 840 return map(file, mode, file.length()); 841 } 842 843 /** 844 * Maps a file in to memory as per 845 * {@link FileChannel#map(java.nio.channels.FileChannel.MapMode, long, long)} 846 * using the requested {@link MapMode}. 847 * 848 * <p>Files are mapped from offset 0 to {@code size}. 849 * 850 * <p>If the mode is {@link MapMode#READ_WRITE} and the file does not exist, 851 * it will be created with the requested {@code size}. Thus this method is 852 * useful for creating memory mapped files which do not yet exist. 853 * 854 * <p>This only works for files <= {@link Integer#MAX_VALUE} bytes. 855 * 856 * @param file the file to map 857 * @param mode the mode to use when mapping {@code file} 858 * @return a buffer reflecting {@code file} 859 * @throws IOException if an I/O error occurs 860 * 861 * @see FileChannel#map(MapMode, long, long) 862 * @since 2.0 863 */ map(File file, MapMode mode, long size)864 public static MappedByteBuffer map(File file, MapMode mode, long size) 865 throws FileNotFoundException, IOException { 866 checkNotNull(file); 867 checkNotNull(mode); 868 869 Closer closer = Closer.create(); 870 try { 871 RandomAccessFile raf = closer.register( 872 new RandomAccessFile(file, mode == MapMode.READ_ONLY ? "r" : "rw")); 873 return map(raf, mode, size); 874 } catch (Throwable e) { 875 throw closer.rethrow(e); 876 } finally { 877 closer.close(); 878 } 879 } 880 map(RandomAccessFile raf, MapMode mode, long size)881 private static MappedByteBuffer map(RandomAccessFile raf, MapMode mode, 882 long size) throws IOException { 883 Closer closer = Closer.create(); 884 try { 885 FileChannel channel = closer.register(raf.getChannel()); 886 return channel.map(mode, 0, size); 887 } catch (Throwable e) { 888 throw closer.rethrow(e); 889 } finally { 890 closer.close(); 891 } 892 } 893 894 /** 895 * Returns the lexically cleaned form of the path name, <i>usually</i> (but 896 * not always) equivalent to the original. The following heuristics are used: 897 * 898 * <ul> 899 * <li>empty string becomes . 900 * <li>. stays as . 901 * <li>fold out ./ 902 * <li>fold out ../ when possible 903 * <li>collapse multiple slashes 904 * <li>delete trailing slashes (unless the path is just "/") 905 * </ul> 906 * 907 * <p>These heuristics do not always match the behavior of the filesystem. In 908 * particular, consider the path {@code a/../b}, which {@code simplifyPath} 909 * will change to {@code b}. If {@code a} is a symlink to {@code x}, {@code 910 * a/../b} may refer to a sibling of {@code x}, rather than the sibling of 911 * {@code a} referred to by {@code b}. 912 * 913 * @since 11.0 914 */ simplifyPath(String pathname)915 public static String simplifyPath(String pathname) { 916 checkNotNull(pathname); 917 if (pathname.length() == 0) { 918 return "."; 919 } 920 921 // split the path apart 922 Iterable<String> components = 923 Splitter.on('/').omitEmptyStrings().split(pathname); 924 List<String> path = new ArrayList<String>(); 925 926 // resolve ., .., and // 927 for (String component : components) { 928 if (component.equals(".")) { 929 continue; 930 } else if (component.equals("..")) { 931 if (path.size() > 0 && !path.get(path.size() - 1).equals("..")) { 932 path.remove(path.size() - 1); 933 } else { 934 path.add(".."); 935 } 936 } else { 937 path.add(component); 938 } 939 } 940 941 // put it back together 942 String result = Joiner.on('/').join(path); 943 if (pathname.charAt(0) == '/') { 944 result = "/" + result; 945 } 946 947 while (result.startsWith("/../")) { 948 result = result.substring(3); 949 } 950 if (result.equals("/..")) { 951 result = "/"; 952 } else if ("".equals(result)) { 953 result = "."; 954 } 955 956 return result; 957 } 958 959 /** 960 * Returns the <a href="http://en.wikipedia.org/wiki/Filename_extension">file 961 * extension</a> for the given file name, or the empty string if the file has 962 * no extension. The result does not include the '{@code .}'. 963 * 964 * @since 11.0 965 */ getFileExtension(String fullName)966 public static String getFileExtension(String fullName) { 967 checkNotNull(fullName); 968 String fileName = new File(fullName).getName(); 969 int dotIndex = fileName.lastIndexOf('.'); 970 return (dotIndex == -1) ? "" : fileName.substring(dotIndex + 1); 971 } 972 973 /** 974 * Returns the file name without its 975 * <a href="http://en.wikipedia.org/wiki/Filename_extension">file extension</a> or path. This is 976 * similar to the {@code basename} unix command. The result does not include the '{@code .}'. 977 * 978 * @param file The name of the file to trim the extension from. This can be either a fully 979 * qualified file name (including a path) or just a file name. 980 * @return The file name without its path or extension. 981 * @since 14.0 982 */ getNameWithoutExtension(String file)983 public static String getNameWithoutExtension(String file) { 984 checkNotNull(file); 985 String fileName = new File(file).getName(); 986 int dotIndex = fileName.lastIndexOf('.'); 987 return (dotIndex == -1) ? fileName : fileName.substring(0, dotIndex); 988 } 989 990 /** 991 * Returns a {@link TreeTraverser} instance for {@link File} trees. 992 * 993 * <p><b>Warning:</b> {@code File} provides no support for symbolic links, and as such there is no 994 * way to ensure that a symbolic link to a directory is not followed when traversing the tree. 995 * In this case, iterables created by this traverser could contain files that are outside of the 996 * given directory or even be infinite if there is a symbolic link loop. 997 * 998 * @since 15.0 999 */ fileTreeTraverser()1000 public static TreeTraverser<File> fileTreeTraverser() { 1001 return FILE_TREE_TRAVERSER; 1002 } 1003 1004 private static final TreeTraverser<File> FILE_TREE_TRAVERSER = new TreeTraverser<File>() { 1005 @Override 1006 public Iterable<File> children(File file) { 1007 // check isDirectory() just because it may be faster than listFiles() on a non-directory 1008 if (file.isDirectory()) { 1009 File[] files = file.listFiles(); 1010 if (files != null) { 1011 return Collections.unmodifiableList(Arrays.asList(files)); 1012 } 1013 } 1014 1015 return Collections.emptyList(); 1016 } 1017 1018 @Override 1019 public String toString() { 1020 return "Files.fileTreeTraverser()"; 1021 } 1022 }; 1023 1024 /** 1025 * Returns a predicate that returns the result of {@link File#isDirectory} on input files. 1026 * 1027 * @since 15.0 1028 */ isDirectory()1029 public static Predicate<File> isDirectory() { 1030 return FilePredicate.IS_DIRECTORY; 1031 } 1032 1033 /** 1034 * Returns a predicate that returns the result of {@link File#isFile} on input files. 1035 * 1036 * @since 15.0 1037 */ isFile()1038 public static Predicate<File> isFile() { 1039 return FilePredicate.IS_FILE; 1040 } 1041 1042 private enum FilePredicate implements Predicate<File> { 1043 IS_DIRECTORY { 1044 @Override apply(File file)1045 public boolean apply(File file) { 1046 return file.isDirectory(); 1047 } 1048 1049 @Override toString()1050 public String toString() { 1051 return "Files.isDirectory()"; 1052 } 1053 }, 1054 1055 IS_FILE { 1056 @Override apply(File file)1057 public boolean apply(File file) { 1058 return file.isFile(); 1059 } 1060 1061 @Override toString()1062 public String toString() { 1063 return "Files.isFile()"; 1064 } 1065 }; 1066 } 1067 } 1068