0
# I/O Utilities
1
2
Comprehensive I/O utilities for streams, files, resources, and data processing with proper exception handling and resource management. These utilities simplify common I/O operations while providing robust error handling.
3
4
## Package: com.google.common.io
5
6
### File Operations
7
8
High-level file operations that handle common tasks like reading, writing, copying, and moving files.
9
10
```java { .api }
11
import com.google.common.io.Files;
12
import java.io.File;
13
import java.nio.charset.StandardCharsets;
14
import java.util.List;
15
16
// Reading files
17
File file = new File("data.txt");
18
byte[] bytes = Files.readBytes(file); // Read entire file as bytes
19
List<String> lines = Files.readLines(file, StandardCharsets.UTF_8); // Read as lines
20
String content = Files.asCharSource(file, StandardCharsets.UTF_8).read(); // Read as string
21
22
// Writing files
23
String data = "Hello, World!";
24
Files.write(data.getBytes(StandardCharsets.UTF_8), file); // Write bytes
25
Files.asCharSink(file, StandardCharsets.UTF_8).write(data); // Write string
26
Files.append(data.getBytes(StandardCharsets.UTF_8), file); // Append bytes
27
28
// Line-by-line processing (memory efficient)
29
Files.asCharSource(file, StandardCharsets.UTF_8).readLines(new LineProcessor<Void>() {
30
@Override
31
public boolean processLine(String line) throws IOException {
32
System.out.println("Processing: " + line);
33
return true; // Continue processing (false to stop)
34
}
35
36
@Override
37
public Void getResult() {
38
return null;
39
}
40
});
41
42
// File operations
43
File source = new File("source.txt");
44
File destination = new File("destination.txt");
45
File directory = new File("backup/");
46
47
Files.copy(source, destination); // Copy file
48
Files.move(source, destination); // Move/rename file
49
boolean identical = Files.equal(source, destination); // Compare file contents
50
51
// File metadata
52
Files.touch(file); // Create empty file or update timestamp
53
File tempDir = Files.createTempDir(); // Create temporary directory
54
55
// File extensions and names
56
String extension = Files.getFileExtension("document.pdf"); // "pdf"
57
String nameWithoutExt = Files.getNameWithoutExtension("document.pdf"); // "document"
58
```
59
60
### Modern Path Operations (MoreFiles)
61
62
Modern NIO.2 Path-based file operations that provide enhanced functionality and better error handling compared to legacy File-based operations.
63
64
```java { .api }
65
import com.google.common.io.MoreFiles;
66
import com.google.common.io.RecursiveDeleteOption;
67
import java.nio.file.Path;
68
import java.nio.file.Paths;
69
import java.nio.charset.StandardCharsets;
70
import java.nio.file.StandardOpenOption;
71
72
// Path-based I/O sources and sinks
73
Path file = Paths.get("data.txt");
74
ByteSource source = MoreFiles.asByteSource(file);
75
ByteSink sink = MoreFiles.asByteSink(file, StandardOpenOption.CREATE);
76
CharSource charSource = MoreFiles.asCharSource(file, StandardCharsets.UTF_8);
77
CharSink charSink = MoreFiles.asCharSink(file, StandardCharsets.UTF_8);
78
79
// Directory listing and traversal
80
Path directory = Paths.get("mydir");
81
ImmutableList<Path> files = MoreFiles.listFiles(directory);
82
83
// File tree traversal
84
Traverser<Path> traverser = MoreFiles.fileTraverser();
85
for (Path path : traverser.breadthFirst(directory)) {
86
System.out.println(path);
87
}
88
89
// Predicates for file filtering
90
Predicate<Path> isDirectory = MoreFiles.isDirectory();
91
Predicate<Path> isRegularFile = MoreFiles.isRegularFile();
92
Predicate<Path> isDirNoFollowLinks = MoreFiles.isDirectory(LinkOption.NOFOLLOW_LINKS);
93
94
// File comparison
95
Path file1 = Paths.get("file1.txt");
96
Path file2 = Paths.get("file2.txt");
97
boolean identical = MoreFiles.equal(file1, file2); // Content comparison
98
99
// File creation and modification
100
Path newFile = Paths.get("newfile.txt");
101
MoreFiles.touch(newFile); // Create empty file or update timestamp
102
103
// Parent directory creation
104
Path nested = Paths.get("deep/nested/path/file.txt");
105
MoreFiles.createParentDirectories(nested); // Creates parent directories if needed
106
107
// File name operations
108
Path document = Paths.get("report.pdf");
109
String extension = MoreFiles.getFileExtension(document); // "pdf"
110
String nameOnly = MoreFiles.getNameWithoutExtension(document); // "report"
111
112
// Recursive deletion operations
113
Path targetDir = Paths.get("old_data");
114
115
// Delete directory and all contents
116
MoreFiles.deleteRecursively(targetDir);
117
118
// Delete with options
119
MoreFiles.deleteRecursively(targetDir,
120
RecursiveDeleteOption.ALLOW_INSECURE); // Allow deletion of insecure paths
121
122
// Delete only directory contents (keep directory)
123
MoreFiles.deleteDirectoryContents(targetDir);
124
125
// Safe deletion with options
126
try {
127
MoreFiles.deleteRecursively(targetDir);
128
} catch (InsecureRecursiveDeleteException e) {
129
// Handle insecure path deletion attempt
130
System.err.println("Deletion blocked for security: " + e.getMessage());
131
}
132
```
133
134
**Key Methods:**
135
- `asByteSource(Path, OpenOption...)` - Create ByteSource for Path
136
- `asByteSink(Path, OpenOption...)` - Create ByteSink for Path
137
- `asCharSource(Path, Charset, OpenOption...)` - Create CharSource for Path
138
- `asCharSink(Path, Charset, OpenOption...)` - Create CharSink for Path
139
- `listFiles(Path)` - List files in directory as ImmutableList
140
- `fileTraverser()` - Create Traverser for file tree walking
141
- `isDirectory(LinkOption...)` - Predicate for directory test
142
- `isRegularFile(LinkOption...)` - Predicate for regular file test
143
- `equal(Path, Path)` - Compare file contents for equality
144
- `touch(Path)` - Create empty file or update timestamp
145
- `createParentDirectories(Path, FileAttribute...)` - Create parent directories
146
- `getFileExtension(Path)` - Extract file extension
147
- `getNameWithoutExtension(Path)` - Get filename without extension
148
- `deleteRecursively(Path, RecursiveDeleteOption...)` - Delete directory tree
149
- `deleteDirectoryContents(Path, RecursiveDeleteOption...)` - Delete directory contents
150
151
**Recursive Delete Options:**
152
- `ALLOW_INSECURE` - Allow deletion of symbolic links and files outside directory tree
153
- Default behavior blocks potentially unsafe deletions for security
154
155
**Advantages over Legacy Files Class:**
156
- Uses modern NIO.2 Path API instead of legacy File
157
- Better error handling and exception specificity
158
- Enhanced security for recursive operations
159
- Support for file system features like symbolic links
160
- More efficient directory traversal
161
- Thread-safe operations
162
163
### Base Encoding (BaseEncoding)
164
165
Binary encoding schemes for reversibly translating between byte sequences and printable ASCII strings. Supports Base64, Base32, Base16 and custom encodings.
166
167
```java { .api }
168
import com.google.common.io.BaseEncoding;
169
import java.nio.charset.StandardCharsets;
170
171
// Standard encodings
172
BaseEncoding base64 = BaseEncoding.base64();
173
BaseEncoding base64Url = BaseEncoding.base64Url(); // URL-safe Base64
174
BaseEncoding base32 = BaseEncoding.base32();
175
BaseEncoding base32Hex = BaseEncoding.base32Hex(); // Base32 with hex digits
176
BaseEncoding base16 = BaseEncoding.base16(); // Hexadecimal
177
178
// Basic encoding and decoding
179
String message = "Hello, World!";
180
byte[] data = message.getBytes(StandardCharsets.UTF_8);
181
182
// Base64 encoding
183
String encoded = base64.encode(data); // "SGVsbG8sIFdvcmxkIQ=="
184
byte[] decoded = base64.decode(encoded);
185
String result = new String(decoded, StandardCharsets.UTF_8); // "Hello, World!"
186
187
// Different encoding types
188
String base32Encoded = base32.encode(data); // "JBSWY3DPEBLW64TMMQQQ===="
189
String hexEncoded = base16.encode(data); // "48656C6C6F2C20576F726C6421"
190
191
// Case handling for Base16
192
String lowerHex = base16.lowerCase().encode(data); // "48656c6c6f2c20576f726c6421"
193
String upperHex = base16.upperCase().encode(data); // "48656C6C6F2C20576F726C6421"
194
195
// Padding control
196
BaseEncoding noPadding = base64.omitPadding();
197
String withoutPadding = noPadding.encode(data); // "SGVsbG8sIFdvcmxkIQ"
198
199
BaseEncoding withPadding = noPadding.withPadChar('=');
200
String restored = withPadding.encode(data); // "SGVsbG8sIFdvcmxkIQ=="
201
202
// Separator insertion for readability
203
BaseEncoding withSeparator = base64.withSeparator("-", 4);
204
String separated = withSeparator.encode(data); // "SGVs-bG8s-IFdv-cmxk-IQ=="
205
206
// Stream-based encoding/decoding
207
CharSink charSink = Files.asCharSink(file, StandardCharsets.UTF_8);
208
ByteSink encodingSink = base64.encodingSink(charSink);
209
encodingSink.write(data); // Writes encoded data to file
210
211
CharSource charSource = Files.asCharSource(file, StandardCharsets.UTF_8);
212
ByteSource decodingSource = base64.decodingSource(charSource);
213
byte[] decodedFromFile = decodingSource.read();
214
215
// Custom alphabets for specialized encoding
216
BaseEncoding customBase32 = BaseEncoding.base32().withPadChar('@');
217
String customEncoded = customBase32.encode(data);
218
219
// Handling encoding without padding requirements
220
try {
221
byte[] decoded = base64.decode("Invalid==Input");
222
} catch (IllegalArgumentException e) {
223
System.err.println("Invalid encoding: " + e.getMessage());
224
}
225
226
// Lenient decoding (ignores whitespace and case differences)
227
BaseEncoding lenient = base16.lowerCase();
228
byte[] result1 = lenient.decode("48656c6c"); // Works
229
byte[] result2 = lenient.decode("48656C6C"); // Also works (case insensitive)
230
```
231
232
**Standard Encodings:**
233
- `base64()` - Standard Base64 encoding (RFC 4648)
234
- `base64Url()` - URL-safe Base64 encoding (uses - and _ instead of + and /)
235
- `base32()` - Base32 encoding (RFC 4648)
236
- `base32Hex()` - Base32 encoding with hex alphabet (0-9, A-V)
237
- `base16()` - Base16 (hexadecimal) encoding
238
239
**Encoding Methods:**
240
- `encode(byte[])` - Encode byte array to string
241
- `encode(byte[], int, int)` - Encode byte array slice
242
- `encodingSink(CharSink)` - Create ByteSink that encodes to CharSink
243
244
**Decoding Methods:**
245
- `decode(CharSequence)` - Decode string to byte array
246
- `decodingSource(CharSource)` - Create ByteSource that decodes from CharSource
247
248
**Customization Methods:**
249
- `omitPadding()` - Remove padding characters from output
250
- `withPadChar(char)` - Use custom padding character
251
- `withSeparator(String, int)` - Insert separator every N characters
252
- `lowerCase()` / `upperCase()` - Control case for applicable encodings
253
254
**Key Features:**
255
- **RFC 4648 Compliant**: Standard implementations of Base64, Base32, Base16
256
- **URL-Safe Variants**: Safe for use in URLs and filenames
257
- **Streaming Support**: Encode/decode via ByteSink/ByteSource abstraction
258
- **Customizable**: Control padding, separators, and case
259
- **Error Handling**: Clear exceptions for invalid input
260
- **Thread-Safe**: All encodings are immutable and thread-safe
261
262
### ByteSource and ByteSink
263
264
Abstractions for reading from and writing to byte-oriented data sources and sinks.
265
266
```java { .api }
267
import com.google.common.io.ByteSource;
268
import com.google.common.io.ByteSink;
269
import java.io.InputStream;
270
import java.io.OutputStream;
271
272
// ByteSource - reading bytes
273
ByteSource source = Files.asByteSource(file);
274
275
// Reading operations
276
byte[] allBytes = source.read(); // Read all bytes
277
long size = source.size(); // Get size if known
278
boolean empty = source.isEmpty(); // Check if empty
279
280
// Stream operations
281
try (InputStream inputStream = source.openStream()) {
282
// Process stream
283
byte[] buffer = new byte[1024];
284
int bytesRead = inputStream.read(buffer);
285
}
286
287
// Copy to other destinations
288
ByteSink sink = Files.asByteSink(destinationFile);
289
source.copyTo(sink); // Copy bytes from source to sink
290
291
OutputStream outputStream = new FileOutputStream("output.dat");
292
source.copyTo(outputStream); // Copy to output stream
293
294
// Slice operations
295
ByteSource slice = source.slice(100, 500); // Bytes 100-599
296
297
// ByteSink - writing bytes
298
ByteSink sink2 = Files.asByteSink(file);
299
sink2.write("Hello World".getBytes(StandardCharsets.UTF_8));
300
301
// Open stream for writing
302
try (OutputStream out = sink2.openStream()) {
303
out.write(data);
304
}
305
306
// Concatenating sources
307
ByteSource combined = ByteSource.concat(source1, source2, source3);
308
byte[] allData = combined.read();
309
310
// Empty source
311
ByteSource empty = ByteSource.empty();
312
```
313
314
### CharSource and CharSink
315
316
Abstractions for reading from and writing to character-oriented data sources and sinks.
317
318
```java { .api }
319
import com.google.common.io.CharSource;
320
import com.google.common.io.CharSink;
321
import java.io.Reader;
322
import java.io.Writer;
323
324
// CharSource - reading characters
325
CharSource source = Files.asCharSource(file, StandardCharsets.UTF_8);
326
327
// Reading operations
328
String content = source.read(); // Read entire content
329
List<String> lines = source.readLines(); // Read as list of lines
330
String firstLine = source.readFirstLine(); // Read just first line
331
long length = source.length(); // Get character count if known
332
333
// Stream operations
334
try (Reader reader = source.openStream()) {
335
char[] buffer = new char[1024];
336
int charsRead = reader.read(buffer);
337
}
338
339
// Line processing (memory efficient for large files)
340
Integer lineCount = source.readLines(new LineProcessor<Integer>() {
341
private int count = 0;
342
343
@Override
344
public boolean processLine(String line) throws IOException {
345
count++;
346
return true; // Continue processing
347
}
348
349
@Override
350
public Integer getResult() {
351
return count;
352
}
353
});
354
355
// Copy operations
356
CharSink sink = Files.asCharSink(destinationFile, StandardCharsets.UTF_8);
357
source.copyTo(sink);
358
359
Writer writer = new FileWriter("output.txt");
360
source.copyTo(writer);
361
362
// CharSink - writing characters
363
CharSink sink2 = Files.asCharSink(file, StandardCharsets.UTF_8);
364
sink2.write("Hello World");
365
sink2.writeLines(Arrays.asList("Line 1", "Line 2", "Line 3"));
366
367
// Open stream for writing
368
try (Writer out = sink2.openStream()) {
369
out.write("Content");
370
}
371
372
// Concatenating sources
373
CharSource combined = CharSource.concat(source1, source2, source3);
374
String allContent = combined.read();
375
376
// Wrapping strings as sources
377
CharSource stringSource = CharSource.wrap("This is a string source");
378
List<String> stringLines = stringSource.readLines();
379
```
380
381
### Resource Operations
382
383
Utilities for working with classpath resources and URLs.
384
385
```java { .api }
386
import com.google.common.io.Resources;
387
import java.net.URL;
388
389
// Getting resource URLs
390
URL configUrl = Resources.getResource("config.properties"); // From classpath root
391
URL relativeUrl = Resources.getResource(MyClass.class, "relative-config.properties"); // Relative to class
392
393
// Reading resources
394
byte[] resourceBytes = Resources.toByteArray(configUrl);
395
String resourceContent = Resources.toString(configUrl, StandardCharsets.UTF_8);
396
List<String> resourceLines = Resources.readLines(configUrl, StandardCharsets.UTF_8);
397
398
// Copying resources
399
Resources.copy(configUrl, new FileOutputStream("local-config.properties"));
400
401
// Resource as ByteSource/CharSource
402
ByteSource resourceByteSource = Resources.asByteSource(configUrl);
403
CharSource resourceCharSource = Resources.asCharSource(configUrl, StandardCharsets.UTF_8);
404
405
// Loading properties from resources
406
Properties props = new Properties();
407
try (InputStream in = Resources.getResource("app.properties").openStream()) {
408
props.load(in);
409
}
410
411
// Utility for resource loading with error handling
412
public class ResourceLoader {
413
public static String loadResourceAsString(String resourcePath) {
414
try {
415
URL resource = Resources.getResource(resourcePath);
416
return Resources.toString(resource, StandardCharsets.UTF_8);
417
} catch (IllegalArgumentException e) {
418
throw new RuntimeException("Resource not found: " + resourcePath, e);
419
} catch (IOException e) {
420
throw new RuntimeException("Failed to read resource: " + resourcePath, e);
421
}
422
}
423
424
public static Properties loadProperties(String resourcePath) {
425
Properties props = new Properties();
426
try {
427
URL resource = Resources.getResource(resourcePath);
428
try (InputStream in = resource.openStream()) {
429
props.load(in);
430
}
431
return props;
432
} catch (Exception e) {
433
throw new RuntimeException("Failed to load properties: " + resourcePath, e);
434
}
435
}
436
}
437
```
438
439
### Stream Utilities
440
441
Low-level utilities for working with input and output streams.
442
443
```java { .api }
444
import com.google.common.io.ByteStreams;
445
import com.google.common.io.CharStreams;
446
import java.io.InputStream;
447
import java.io.OutputStream;
448
import java.io.Reader;
449
import java.io.Writer;
450
451
// Byte stream operations
452
InputStream input = new FileInputStream("source.dat");
453
OutputStream output = new FileOutputStream("destination.dat");
454
455
// Copy entire stream
456
long bytesCopied = ByteStreams.copy(input, output);
457
458
// Read operations
459
byte[] buffer = new byte[1024];
460
int bytesRead = ByteStreams.read(input, buffer, 0, buffer.length); // Read exactly
461
ByteStreams.readFully(input, buffer); // Read exactly buffer.length bytes
462
ByteStreams.skipFully(input, 100); // Skip exactly 100 bytes
463
464
// Read entire stream to byte array
465
byte[] allBytes = ByteStreams.toByteArray(input);
466
467
// Exhaust stream (read and discard all data)
468
ByteStreams.exhaust(input);
469
470
// Null output stream (discards all data)
471
OutputStream nullOut = ByteStreams.nullOutputStream();
472
473
// Character stream operations
474
Reader reader = new FileReader("source.txt");
475
Writer writer = new FileWriter("destination.txt");
476
477
// Copy entire stream
478
long charsCopied = CharStreams.copy(reader, writer);
479
480
// Read operations
481
String content = CharStreams.toString(reader); // Read entire stream as string
482
List<String> lines = CharStreams.readLines(reader); // Read as list of lines
483
CharStreams.skipFully(reader, 50); // Skip exactly 50 characters
484
485
// Exhaust stream
486
CharStreams.exhaust(reader);
487
488
// Null writer (discards all data)
489
Writer nullWriter = CharStreams.nullWriter();
490
491
// Limited streams
492
InputStream limited = ByteStreams.limit(originalInputStream, 1024); // Limit to 1024 bytes
493
```
494
495
### Resource Management
496
497
Utilities for proper resource cleanup and exception handling.
498
499
```java { .api }
500
import com.google.common.io.Closer;
501
import com.google.common.io.Closeables;
502
import com.google.common.io.Flushables;
503
504
// Closer - manages multiple resources
505
Closer closer = Closer.create();
506
try {
507
InputStream in = closer.register(new FileInputStream("input.txt"));
508
OutputStream out = closer.register(new FileOutputStream("output.txt"));
509
Reader reader = closer.register(new InputStreamReader(in, StandardCharsets.UTF_8));
510
Writer writer = closer.register(new OutputStreamWriter(out, StandardCharsets.UTF_8));
511
512
// Use resources
513
CharStreams.copy(reader, writer);
514
515
} catch (IOException e) {
516
throw closer.rethrow(e); // Properly handle exceptions
517
} finally {
518
closer.close(); // Closes all registered resources
519
}
520
521
// Simple resource cleanup
522
FileInputStream input = null;
523
try {
524
input = new FileInputStream("file.txt");
525
// Use input stream
526
} finally {
527
Closeables.closeQuietly(input); // Closes and ignores exceptions
528
}
529
530
// Close with exception propagation
531
FileOutputStream output = null;
532
try {
533
output = new FileOutputStream("file.txt");
534
// Use output stream
535
} catch (IOException e) {
536
Closeables.close(output, true); // Close and suppress exceptions
537
throw e;
538
} catch (RuntimeException e) {
539
Closeables.close(output, false); // Close and propagate exceptions
540
throw e;
541
} finally {
542
Closeables.close(output, true);
543
}
544
545
// Flushing with error handling
546
Writer writer = new FileWriter("output.txt");
547
try {
548
writer.write("data");
549
Flushables.flush(writer, false); // Flush and propagate exceptions
550
} catch (IOException e) {
551
Flushables.flushQuietly(writer); // Best effort flush
552
throw e;
553
}
554
```
555
556
### Specialized Streams
557
558
Enhanced input and output streams with additional capabilities.
559
560
```java { .api }
561
import com.google.common.io.CountingInputStream;
562
import com.google.common.io.CountingOutputStream;
563
import com.google.common.io.LittleEndianDataInputStream;
564
import com.google.common.io.LittleEndianDataOutputStream;
565
566
// Counting streams - track bytes read/written
567
InputStream original = new FileInputStream("data.bin");
568
CountingInputStream counting = new CountingInputStream(original);
569
570
byte[] buffer = new byte[1024];
571
counting.read(buffer);
572
long bytesRead = counting.getCount(); // Number of bytes read so far
573
574
// Counting output stream
575
OutputStream originalOut = new FileOutputStream("output.bin");
576
CountingOutputStream countingOut = new CountingOutputStream(originalOut);
577
578
countingOut.write("Hello".getBytes());
579
long bytesWritten = countingOut.getCount(); // Number of bytes written
580
581
// Little-endian data streams (for binary protocol compatibility)
582
LittleEndianDataInputStream littleEndianIn = new LittleEndianDataInputStream(inputStream);
583
int value = littleEndianIn.readInt(); // Reads int in little-endian format
584
long longValue = littleEndianIn.readLong();
585
float floatValue = littleEndianIn.readFloat();
586
587
LittleEndianDataOutputStream littleEndianOut = new LittleEndianDataOutputStream(outputStream);
588
littleEndianOut.writeInt(42); // Writes int in little-endian format
589
littleEndianOut.writeLong(12345L);
590
littleEndianOut.writeFloat(3.14f);
591
```
592
593
### Processing Patterns
594
595
Common patterns for processing data with I/O utilities.
596
597
```java { .api }
598
// Batch processing large files
599
public void processLargeFile(File file) throws IOException {
600
Files.asCharSource(file, StandardCharsets.UTF_8).readLines(new LineProcessor<Void>() {
601
private int lineCount = 0;
602
603
@Override
604
public boolean processLine(String line) throws IOException {
605
lineCount++;
606
607
// Process each line
608
String processed = processLine(line);
609
610
// Write to output or accumulate results
611
if (processed != null) {
612
writeToOutput(processed);
613
}
614
615
// Progress reporting
616
if (lineCount % 10000 == 0) {
617
System.out.println("Processed " + lineCount + " lines");
618
}
619
620
return true; // Continue processing
621
}
622
623
@Override
624
public Void getResult() {
625
System.out.println("Total lines processed: " + lineCount);
626
return null;
627
}
628
});
629
}
630
631
// Safe file operations with backup
632
public void safeFileWrite(File file, String content) throws IOException {
633
File backupFile = new File(file.getAbsolutePath() + ".backup");
634
File tempFile = new File(file.getAbsolutePath() + ".tmp");
635
636
try {
637
// Create backup if original exists
638
if (file.exists()) {
639
Files.copy(file, backupFile);
640
}
641
642
// Write to temporary file first
643
Files.asCharSink(tempFile, StandardCharsets.UTF_8).write(content);
644
645
// Atomic move to final location
646
Files.move(tempFile, file);
647
648
// Clean up backup on success
649
if (backupFile.exists()) {
650
backupFile.delete();
651
}
652
653
} catch (IOException e) {
654
// Clean up temp file on failure
655
if (tempFile.exists()) {
656
tempFile.delete();
657
}
658
659
// Restore from backup if available
660
if (backupFile.exists() && !file.exists()) {
661
Files.move(backupFile, file);
662
}
663
664
throw e;
665
}
666
}
667
668
// Stream transformation pipeline
669
public void transformData(InputStream input, OutputStream output) throws IOException {
670
Closer closer = Closer.create();
671
try {
672
// Create transformation pipeline
673
Reader reader = closer.register(new InputStreamReader(input, StandardCharsets.UTF_8));
674
Writer writer = closer.register(new OutputStreamWriter(output, StandardCharsets.UTF_8));
675
676
// Process data line by line
677
BufferedReader buffered = closer.register(new BufferedReader(reader));
678
PrintWriter printer = closer.register(new PrintWriter(writer));
679
680
String line;
681
while ((line = buffered.readLine()) != null) {
682
String transformed = transformLine(line);
683
if (transformed != null) {
684
printer.println(transformed);
685
}
686
}
687
688
printer.flush();
689
690
} catch (IOException e) {
691
throw closer.rethrow(e);
692
} finally {
693
closer.close();
694
}
695
}
696
697
// Binary data processing
698
public void processBinaryData(ByteSource source, ByteSink sink) throws IOException {
699
try (InputStream in = source.openBufferedStream();
700
OutputStream out = sink.openBufferedStream()) {
701
702
CountingInputStream countingIn = new CountingInputStream(in);
703
CountingOutputStream countingOut = new CountingOutputStream(out);
704
705
byte[] buffer = new byte[8192];
706
int bytesRead;
707
708
while ((bytesRead = countingIn.read(buffer)) != -1) {
709
// Process buffer if needed
710
byte[] processed = processBytes(buffer, 0, bytesRead);
711
countingOut.write(processed);
712
}
713
714
System.out.println("Processed " + countingIn.getCount() + " bytes");
715
System.out.println("Wrote " + countingOut.getCount() + " bytes");
716
}
717
}
718
```
719
720
### Testing I/O Code
721
722
Utilities and patterns for testing I/O operations.
723
724
```java { .api }
725
// Testing with temporary files
726
@Test
727
public void testFileProcessing() throws IOException {
728
File tempDir = Files.createTempDir();
729
File testFile = new File(tempDir, "test.txt");
730
731
try {
732
// Setup test data
733
List<String> testLines = Arrays.asList("line1", "line2", "line3");
734
Files.asCharSink(testFile, StandardCharsets.UTF_8).writeLines(testLines);
735
736
// Test the processing
737
processFile(testFile);
738
739
// Verify results
740
List<String> resultLines = Files.readLines(testFile, StandardCharsets.UTF_8);
741
assertEquals(expected, resultLines);
742
743
} finally {
744
// Cleanup
745
if (tempDir.exists()) {
746
deleteRecursively(tempDir);
747
}
748
}
749
}
750
751
// Testing with in-memory sources/sinks
752
@Test
753
public void testDataTransformation() throws IOException {
754
String inputData = "input line 1\ninput line 2\n";
755
CharSource source = CharSource.wrap(inputData);
756
757
StringBuilder output = new StringBuilder();
758
CharSink sink = CharSink.wrap(output);
759
760
transformData(source, sink);
761
762
String result = output.toString();
763
assertEquals("expected output", result);
764
}
765
```
766
767
### Hash-Aware I/O Streams
768
769
I/O streams that compute hash codes while reading or writing data.
770
771
```java { .api }
772
import com.google.common.io.HashingInputStream;
773
import com.google.common.io.HashingOutputStream;
774
import com.google.common.hash.Hashing;
775
import com.google.common.hash.HashCode;
776
777
// Hash data while reading
778
InputStream originalInput = new FileInputStream("data.bin");
779
HashingInputStream hashingInput = new HashingInputStream(Hashing.sha256(), originalInput);
780
781
byte[] buffer = new byte[1024];
782
int bytesRead;
783
while ((bytesRead = hashingInput.read(buffer)) != -1) {
784
// Process data
785
}
786
787
HashCode inputHash = hashingInput.hash(); // SHA-256 of all data read
788
originalInput.close();
789
790
// Hash data while writing
791
OutputStream originalOutput = new FileOutputStream("output.bin");
792
HashingOutputStream hashingOutput = new HashingOutputStream(Hashing.md5(), originalOutput);
793
794
hashingOutput.write("Hello World".getBytes(StandardCharsets.UTF_8));
795
hashingOutput.flush();
796
797
HashCode outputHash = hashingOutput.hash(); // MD5 of all data written
798
originalOutput.close();
799
800
// Verify data integrity during copy
801
public void copyWithVerification(File source, File destination) throws IOException {
802
try (InputStream in = new FileInputStream(source);
803
OutputStream out = new FileOutputStream(destination)) {
804
805
HashingInputStream hashingIn = new HashingInputStream(Hashing.sha256(), in);
806
HashingOutputStream hashingOut = new HashingOutputStream(Hashing.sha256(), out);
807
808
ByteStreams.copy(hashingIn, hashingOut);
809
810
HashCode sourceHash = hashingIn.hash();
811
HashCode destHash = hashingOut.hash();
812
813
if (!sourceHash.equals(destHash)) {
814
throw new IOException("Hash verification failed during copy");
815
}
816
}
817
}
818
```
819
820
### Processing Interfaces
821
822
Callback interfaces for processing data streams.
823
824
```java { .api }
825
import com.google.common.io.LineProcessor;
826
import com.google.common.io.ByteProcessor;
827
828
// Line processor for text data
829
public class WordCountProcessor implements LineProcessor<Integer> {
830
private int wordCount = 0;
831
832
@Override
833
public boolean processLine(String line) throws IOException {
834
if (!line.trim().isEmpty()) {
835
String[] words = line.trim().split("\\s+");
836
wordCount += words.length;
837
}
838
return true; // Continue processing
839
}
840
841
@Override
842
public Integer getResult() {
843
return wordCount;
844
}
845
}
846
847
// Usage
848
File textFile = new File("document.txt");
849
Integer totalWords = Files.asCharSource(textFile, StandardCharsets.UTF_8)
850
.readLines(new WordCountProcessor());
851
852
// Byte processor for binary data
853
public class ChecksumProcessor implements ByteProcessor<String> {
854
private final Hasher hasher = Hashing.crc32().newHasher();
855
856
@Override
857
public boolean processBytes(byte[] buf, int off, int len) throws IOException {
858
hasher.putBytes(buf, off, len);
859
return true; // Continue processing
860
}
861
862
@Override
863
public String getResult() {
864
return hasher.hash().toString();
865
}
866
}
867
868
// Usage
869
ByteSource source = Files.asByteSource(new File("data.bin"));
870
String checksum = source.read(new ChecksumProcessor());
871
```
872
873
**Additional I/O Classes:**
874
875
- `HashingInputStream` - InputStream that maintains running hash of data read
876
- `HashingOutputStream` - OutputStream that maintains running hash of data written
877
- `LineProcessor<T>` - Interface for processing text files line by line
878
- `ByteProcessor<T>` - Interface for processing byte arrays with result accumulation
879
880
Guava's I/O utilities provide a comprehensive, safe, and efficient way to handle file operations, stream processing, and resource management with proper exception handling and cleanup patterns.