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