Java开源工具库使用之Apache Commons IO

前言

Apache Commons IO 是 Apache 软件基金会的一个开源项目,提供了一组用于处理 I/O 操作的实用工具类。它的目标是简化常见的文件和流操作,提供更方便、更高效的方式来处理输入和输出。

github 地址:https://github.com/apache/commons-io

官网地址:https://commons.apache.org/proper/commons-io/

pom依赖:

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.13.0</version>
</dependency>

一、IO

1.1 IO流的读取/关闭

InputStream in = new URL( "https://commons.apache.org" ).openStream();
try {
    
    
    // 将流内内容转换为字符串
    System.out.println(IOUtils.toString(in, Charset.defaultCharset()));
} finally {
    
    
    // 安全关闭流
    IOUtils.closeQuietly(in);
}

inputStream = new FileInputStream("file.txt");
try {
    
    
    // 消耗输入流内容,并忽视它
    IOUtils.consume(inputStream);
} finally {
    
    
    IOUtils.closeQuietly(inputStream);
}

1.2 IO流相等

try (InputStream inputStream1 = Files.newInputStream(file1.toPath());
    InputStream inputStream2 = Files.newInputStream(file2.toPath())) {
    
    
    // 比较流内容相等,还有个方法contentEqualsIgnoreEO,可以忽略行尾换行符,windows/linux/mac文件差异
    boolean contentEquals = IOUtils.contentEquals(inputStream1, inputStream2);
    if (contentEquals) {
    
    
        System.out.println("文件内容相同");
    } else {
    
    
        System.out.println("文件内容不同");
    }
} catch (IOException e) {
    
    
    // 处理异常
}

1.3 IO流复制

File sourceFile = new File("source.txt");
File targetFile = new File("target.txt");

try (InputStream inputStream = Files.newInputStream(sourceFile.toPath());
     OutputStream outputStream = Files.newOutputStream(targetFile.toPath())) {
    
    
    // 从一个输入流复制数据到一个输出流,数据量大(超过2G)可以使用 copyLarge
    long bytesCopied = IOUtils.copy(inputStream, outputStream);
    System.out.println("已复制 " + bytesCopied + " 字节");
} catch (IOException e) {
    
    
    // 处理异常
} 

1.4 IO流缓冲

// 将输入流或输出流包装成带有缓冲功能的流
// 作用是为了优化数据的读取和写入性能。对于大量数据的读取和写入操作,使用缓冲流可以减少对底层资源(如磁盘)的频繁访问,从而提高整体的执行效率。
InputStream inputStream = new FileInputStream("input.txt");
BufferedInputStream bufferedInputStream = IOUtils.buffer(inputStream);

1.5 文件行读写

File file = new File("file.txt");

try (LineIterator iterator = IOUtils.lineIterator(new FileReader(file))) {
    
    
    while (iterator.hasNext()) {
    
    
        String line = iterator.next();
        // 处理每行文本内容
        System.out.println(line);
    }
} catch (IOException e) {
    
    
    // 处理异常
}



// 读取文件所有行
List<String> lines = IOUtils.readLines(new FileReader(file));

// 将每行内容写入文件
List<String> lines = new ArrayList<>();
lines.add("Line 1");
lines.add("Line 2");
lines.add("Line 3");

File outputFile = new File("output.txt");

try (OutputStream outputStream = Files.newOutputStream(outputFile.toPath());
     BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
    
    
     IOUtils.writeLines(lines, System.lineSeparator(), writer);
} catch (IOException e) {
    
    
    // 处理异常
}

二、文件

2.1 读写文件

// 读文件
FileUtils.readFileToString(file1, Charset.defaultCharset());

// 写文件
List<String> lines = new ArrayList<>();
lines.add("Line 1");
lines.add("Line 2");
lines.add("Line 3");

File outputFile = new File("output.txt");
FileUtils.writeLines(outputFile, StandardCharsets.UTF_8.name(), lines);

2.2 目录下筛选文件

// 获取当前目录下,扩展名为xml和java的文件,true表示递归扩展子文件夹
Collection<File> files = FileUtils.listFiles(new File("."), new String[]{
    
    "xml", "java"}, true);
System.out.println(files);

2.3 复制移动文件/目录

// 复制文件
FileUtils.copyFile(new File("pom.xml"), new File("../pom.xml.bak"));
// 复制目录
FileUtils.copyDirectory(new File("."), new File("../test"));


// 移动文件
FileUtils.moveFile(new File("pom.xml"), new File("../pom.xml"));
// 移动文件到目录中,true为创建目录
FileUtils.moveFileToDirectory(new File("pom.xml"), new File("F://dic"), true);
// 移动到目录,可以是文件,也可以是目录
FileUtils.moveToDirectory(new File("D://test"), new File("F://test"), true);

// 移动目录
FileUtils.moveDirectory(new File("D://test"), new File("F://test"));
// 移动目录,true表示创建目的目录
FileUtils.moveDirectoryToDirectory(new File("D://test"), new File("F://test"), true);

2.4 删除文件/目录

// 删除文件,不能删除会抛异常
FileUtils.delete(new File("1.txt"));

// 删除目录
FileUtils.deleteDirectory(new File("../test"));

// 删除文件或目录,目录不能为空,不能删除时抛异常
FileUtils.forceDelete(new File("../test"));

2.5 比较文件修改时间


boolean isFileOlder = FileUtils.isFileOlder(new File("pom.xml"), LocalDateTime.of(2023, 8, 29, 12, 12, 12));
System.out.println(isFileOlder);

boolean isFileNewer = FileUtils.isFileNewer(new File("pom.xml"), LocalDateTime.of(2023, 8, 29, 12, 12, 12));
System.out.println(isFileNewer);

2.6 获取文件目录大小

long size = FileUtils.sizeOf(new File("pom.xml"));
System.out.println(size);
System.out.println(FileUtils.byteCountToDisplaySize(size));

long directorySize = FileUtils.sizeOfDirectory(new File("../"));
System.out.println(directorySize);
System.out.println(FileUtils.byteCountToDisplaySize(directorySize));

2.7 计算checksum

long checksumCRC32 = FileUtils.checksumCRC32(new File("pom.xml"));
System.out.println(checksumCRC32);

2.8 文件内容比较

FileUtils.contentEquals(new File("1.txt"), new File("1.txt"));
FileUtils.contentEqualsIgnoreEOL(new File("1.txt"), new File("1.txt"), StandardCharsets.UTF_8.name());

2.9 文件监视

// 定义一个监听类
class MyFileListener extends FileAlterationListenerAdaptor {
    
    
    // 文件创建事件
    @Override
    public void onFileCreate(File file) {
    
    
        System.out.println("文件创建:" + file.getAbsolutePath());
    }

    // 文件修改事件
    @Override
    public void onFileChange(File file) {
    
    
        System.out.println("文件修改:" + file.getAbsolutePath());
    }

    // 文件删除事件
    @Override
    public void onFileDelete(File file) {
    
    
        System.out.println("文件删除:" + file.getAbsolutePath());
    }
}

final long interval = 100;
final String testDir = "D://test";

// 建立一个观察者
final FileAlterationObserver observer = new FileAlterationObserver(testDir);
observer.addListener(new MyFileListener());

// 建立一个监控者,监控间隔,单位毫秒
final FileAlterationMonitor monitor = new FileAlterationMonitor(interval);
monitor.addObserver(observer);
monitor.start();


// 创建文件
File file = new File(testDir, "file.java");
FileUtils.touch(file);

ThreadUtils.sleep(Duration.ofSeconds(2));

// 更新文件
FileUtils.write(file, "testtest测试测试", Charset.defaultCharset());

ThreadUtils.sleep(Duration.ofSeconds(2));

// 删除文件
FileUtils.delete(file);

ThreadUtils.sleep(Duration.ofSeconds(2));

// 停止监控
monitor.stop();

2.10 文件排序比较器

  • NameFileComparator:按文件名进行比较排序。
  • PathFileComparator:根据文件路径进行比较排序。
  • SizeFileComparator:按文件大小进行比较排序。
  • LastModifiedFileComparator:按文件的最后修改时间进行比较排序。
  • ExtensionFileComparator:按文件扩展名进行比较排序。
final String testDir = "D://test";

Collection<File> listFiles = FileUtils.listFiles(new File(testDir), TrueFileFilter.INSTANCE, null);

// 使用不同的比较器进行排序
File[] sortedFiles;

// 按文件名排序(忽略大小写)
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, NameFileComparator.NAME_INSENSITIVE_COMPARATOR);
System.out.println("按文件名排序(忽略大小写):");
System.out.println(Arrays.toString(sortedFiles));

// 按文件路径排序
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, PathFileComparator.PATH_COMPARATOR);
System.out.println("按文件路径排序:");
System.out.println(Arrays.toString(sortedFiles));

// 按文件大小排序(从小到大)
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, SizeFileComparator.SIZE_COMPARATOR);
System.out.println("按文件大小排序(从小到大):");
System.out.println(Arrays.toString(sortedFiles));

// 按文件最后修改时间排序
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
System.out.println("按文件最后修改时间排序:");
System.out.println(Arrays.toString(sortedFiles));

// 按文件扩展名排序
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, ExtensionFileComparator.EXTENSION_COMPARATOR);
System.out.println("按文件扩展名排序:");
System.out.println(Arrays.toString(sortedFiles));

2.11 文件过滤器

  • FileFilter: 用于过滤文件,可以根据文件的属性、名称等进行过滤。
  • IOFileFilter: 继承自 FileFilter 接口,提供更多的过滤方法来过滤文件和目录。
  • TrueFileFilter: IOFileFilter 的一个实现,始终返回 true,表示接受所有文件。
  • FalseFileFilter: IOFileFilter 的一个实现,始终返回 false,表示拒绝所有文件。
  • SuffixFileFilter: 根据文件后缀进行过滤。
  • PrefixFileFilter: 根据文件前缀进行过滤。
  • WildcardFileFilter: 使用通配符模式进行过滤。
  • NameFileFilter: 按文件名进行过滤。
  • AndFileFilter: 对多个过滤器使用逻辑与操作。
  • OrFileFilter: 对多个过滤器使用逻辑或操作。
// 创建一个过滤器链,过滤出目录中符合条件的文件
IOFileFilter filter = FileFilterUtils.and(
    FileFilterUtils.sizeFileFilter(100), // 大小超过100字节
    FileFilterUtils.or(
        FileFilterUtils.suffixFileFilter(".txt"), // 后缀为.txt
        FileFilterUtils.prefixFileFilter("file") // 前缀为file
    )
);

// 使用过滤器链来获取目录中符合条件的文件集合
Collection<File> filterFiles = FileUtils.listFiles(new File(testDir), filter, TrueFileFilter.INSTANCE);

// 打印过滤后的文件列表
System.out.println("文件过滤器");
for (File file : filterFiles) {
    
    
    System.out.println(file.getAbsolutePath());
}

三、文件名

3.1 获取文件基本名/扩展名

final String testDir = "D://test";
final String testFile = "D://test.java";

String extension = FilenameUtils.getExtension(testFile);
String baseName = FilenameUtils.getBaseName(testFile);
String name = FilenameUtils.getName(testFile);
System.out.println(String.format("%s, %s, %s", extension, baseName, name));

3.2 文件名规格化

String normalize = FilenameUtils.normalize("C://test/../c/././d/e");
System.out.println(normalize); // C:\c\d\e

String normalize1 = FilenameUtils.normalize("../test/c");
System.out.println(normalize1); // null

3.3 文件名连接,包含

String basePath = "C://test";
String filename = "file.java";
String concatFile = FilenameUtils.concat(basePath, filename);
System.out.println(concatFile); // C:\test\file.java


String directory = "C:\\a";
String child = "C:\\a\\test.java";
boolean contains = FilenameUtils.directoryContains(directory, child);
System.out.println(contains);

3.4 文件名匹配

// *0或多个字符,?1个字符,
boolean match = FilenameUtils.wildcardMatch(child, "*.java");
System.out.println(match);

四、Stream流

4.1 空流

NullOutputStream 用于丢弃所有写入的数据,即相当于一个无操作的输出流使用,主要场景是在需要一个输出流对象,但实际上并不需要对数据进行任何的输出时,可以使用 NullOutputStream 来代替其他具体的输出流类,例如 FileOutputStreamByteArrayOutputStream

使用 NullOutputStream 可以避免创建真实的输出流对象,从而节省资源和提高性能。同时,它还可以在某些情况下用作空操作的占位符,使代码逻辑更加清晰。

OutputStream outputStream = NullOutputStream.INSTANCE;
// 通过 NullOutputStream 对象写入数据
outputStream.write("Hello, world!".getBytes());
// 关闭输出流
outputStream.close();

4.2 计数流

CountingOutputStream计算通过该流写入的字节数。每次写入数据时,CountingOutputStream 会自动更新内部的计数器。

通过使用 CountingOutputStream,我们可以方便地计算输出了多少字节的数据,这在某些场景下可能是很有用的,比如需要统计文件的大小或者传输的数据量等。

// 创建一个输出流,用于写入数据
OutputStream outputStream = Files.newOutputStream(Paths.get("D://file.txt"));

// 创建一个 CountingOutputStream 对象,将其包装在原始的输出流上
CountingOutputStream countingOutputStream = new CountingOutputStream(outputStream);

// 写入数据到 CountingOutputStream
countingOutputStream.write("Hello, world!".getBytes());

// 获取计数器的值
long byteCount = countingOutputStream.getByteCount();
System.out.println(byteCount);

// 再次写入,获取累加值
countingOutputStream.write("中文字节数".getBytes());
byteCount = countingOutputStream.getByteCount();
System.out.println(byteCount);

// 关闭 CountingOutputStream(会自动关闭原始的输出流)
countingOutputStream.close();

4.3 延迟写入流

DeferredFileOutputStream 将数据写入到内存中的缓冲区,并在达到指定阈值后,自动将数据写入到临时文件中。这种延迟写入的机制可以减少内存占用,特别适用于处理大量数据或大文件的场景。

String testString = "0123456789";
byte[] testBytes = testString.getBytes();
int initialBufferSize = 1024;
final String prefix = "commons-io-test";
final String suffix = ".out";
final Path tempDir = Paths.get("target");
File outFile = new File("testAboveThreshold.dat");

final DeferredFileOutputStream dfos = DeferredFileOutputStream.builder()
    .setThreshold(testBytes.length - 5)
    .setBufferSize(initialBufferSize)
    .setPrefix(prefix)
    .setSuffix(suffix)
    .setDirectory(tempDir.toFile())
    .setOutputFile(outFile)
    .get();

dfos.write(testBytes, 0, testBytes.length);
dfos.close();

4.4 分支输出流

TeeOutputStream 可以将数据同时写入多个输出流中。这对于需要将数据同时写入到多个目标的情况非常有用,例如日志记录或数据备份等场景。

// 创建两个 FileOutputStream 对象,作为目标输出流
FileOutputStream outputStream1 = new FileOutputStream("file1.txt");
FileOutputStream outputStream2 = new FileOutputStream("file2.txt");

// 创建一个 TeeOutputStream 对象,将数据同时写入两个输出流
TeeOutputStream teeOutputStream = new TeeOutputStream(outputStream1, outputStream2);

// 写入数据到 TeeOutputStream
teeOutputStream.write("Hello, world!".getBytes());

// 关闭 TeeOutputStream
teeOutputStream.close();

// 关闭目标输出流
outputStream1.close();
outputStream2.close();

4.5 不关闭流

CloseShieldOutputStream 包装了一个底层的输出流,并提供了一种机制来屏蔽关闭操作。这意味着关闭 CloseShieldOutputStream 不会关闭底层的输出流。

// 创建一个 FileOutputStream 对象作为底层输出流
FileOutputStream outputStream = new FileOutputStream("file.txt");

// 创建一个 CloseShieldOutputStream 对象,将底层输出流包装起来
CloseShieldOutputStream closeShieldOutputStream = CloseShieldOutputStream.wrap(outputStream);

// 写入数据到 CloseShieldOutputStream
closeShieldOutputStream.write("Hello, world!".getBytes());

// 关闭 CloseShieldOutputStream(不会关闭底层输出流)
closeShieldOutputStream.close();

// 关闭底层输出流
outputStream.close();

五、字节序大小端

在多数 Intel 架构的计算机上,使用小端字节顺序;而在一些网络协议中,常使用大端字节顺序。

5.1 原始属性转换

double value = 3.14159;

// 将双精度浮点数的字节顺序进行交换,
double swappedValue = EndianUtils.swapDouble(value);

System.out.println(swappedValue);  // 输出:2.5574005185030673E224

5.2 流字节转换

// 将小端序转化为大端序(Big-Endian)
// 将双精度浮点数 3.14159 写入字节数组
long value = Double.doubleToRawLongBits(3.14159);
bytes[7] = (byte) ((value >> 56) & 0xFF);
bytes[6] = (byte) ((value >> 48) & 0xFF);
bytes[5] = (byte) ((value >> 40) & 0xFF);
bytes[4] = (byte) ((value >> 32) & 0xFF);
bytes[3] = (byte) ((value >> 24) & 0xFF);
bytes[2] = (byte) ((value >> 16) & 0xFF);
bytes[1] = (byte) ((value >> 8) & 0xFF);
bytes[0] = (byte) (value & 0xFF);

// 从字节数组中读取双精度浮点数(交换字节顺序)
double result = EndianUtils.readSwappedDouble(bytes, 0);

System.out.println(result);  // 输出: 3.14159

// 文件流字节序转换
EndianUtils.readSwappedInteger(Files.newInputStream(Paths.get("1.txt")));

参考

  1. https://github.com/apache/commons-io

猜你喜欢

转载自blog.csdn.net/qq_23091073/article/details/132570698