NIO+正则实战🔥 搞定文件IO与文本清洗,新手也能直接上手

13 阅读6分钟

NIO+正则实战🔥 搞定文件IO与文本清洗,新手也能直接上手

核心速通:精简实用,保留核心知识点和可复制代码,新增多样样式,新手快速上手避坑,无需死记硬背

Java开发常遇3大痛点,无需死磕:读取大文件OOM、跨平台路径报错、文本提取清洗繁琐。NIO+正则就是“万能钥匙”,全程实战无废话,代码复制就能用,看完轻松拿捏。

先划重点:吃透NIO核心用法+分清NIO与BIO适用场景;掌握Pattern/Matcher实战技巧,文本处理秒速搞定,省出时间摸鱼不香吗?

不啰嗦,直接开冲!先搞定文件IO的“重灾区”——NIO,再轻松拿捏正则。

一、NIO核心概念详解:告别BIO低效,解锁高效新姿势

NIO(非阻塞IO)是BIO的升级版,核心优势:非阻塞、面向缓冲区/通道,解决BIO线程阻塞的痛点。用大白话讲:BIO是“排队等奶茶”(线程卡死),NIO是“取号后自由活动”(线程可处理其他任务),效率翻倍。

重点拆解4个核心组件,结合可复制代码,懂用法就够了:

1. Path:路径神器(替代File类)

自动适配Windows/Linux路径分隔符,拼接、解析简洁,仅表示路径,操作文件需配合Files类。

import java.nio.file.Path;
import java.nio.file.Paths;

public class PathDemo {
    public static void main(String[] args) {
        Path absolutePath = Paths.get("D:/test.txt"); // 绝对路径
        Path relativePath = Paths.get("src", "main", "resources"); // 相对路径
        System.out.println("文件名:" + absolutePath.getFileName());
        System.out.println("路径拼接:" + relativePath.resolve("config.properties"));
    }
}

2. Files:文件操作万能工具

静态方法一键搞定文件CRUD,避免冗余代码,减少异常踩坑,新手首选。

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.charset.StandardCharsets;
import java.util.List;

public class FilesDemo {
    public static void main(String[] args) throws Exception {
        Path path = Paths.get("test.txt");
        if (!Files.exists(path)) Files.createFile(path); // 不存在则创建
        Files.write(path, "Hello NIO".getBytes(StandardCharsets.UTF_8)); // 写入(防乱码)
        List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8); // 读取
        Files.copy(path, Paths.get("test_copy.txt")); // 复制
        Files.deleteIfExists(path); // 存在则删除
    }
}

3. ByteBuffer:NIO核心数据容器

重点记:3个指针(position/limit/capacity)+3个方法(flip()/rewind()/clear()),写一遍就懂!

import java.nio.ByteBuffer;

public class ByteBufferDemo {
    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(10); // 非直接缓冲区
        buffer.put("abc".getBytes()); // 写模式
        buffer.flip(); // 写转读,避免读多数据
        byte[] dst = new byte[buffer.remaining()]; // 获取可读取字节数
        buffer.get(dst);
        System.out.println(new String(dst)); // 输出:abc
        buffer.clear(); // 清空指针,准备重新写入
    }
}

4. Channels:双向数据传输管道

替代BIO单向流,支持双向传输+非阻塞,常用FileChannel处理文件读写,效率更高。

import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class ChannelDemo {
    public static void main(String[] args) throws Exception {
        RandomAccessFile file = new RandomAccessFile("test_channel.txt", "rw");
        FileChannel channel = file.getChannel();
        // 写入:缓冲区->通道->文件
        ByteBuffer writeBuffer = ByteBuffer.allocate(1024);
        writeBuffer.put("Hello Channel".getBytes());
        writeBuffer.flip();
        channel.write(writeBuffer);
        // 读取:文件->通道->缓冲区
        channel.position(0);
        ByteBuffer readBuffer = ByteBuffer.allocate(1024);
        int readBytes = channel.read(readBuffer);
        System.out.println(new String(readBuffer.array(), 0, readBytes));
        // 关闭资源(避免泄漏)
        channel.close();
        file.close();
    }
}

二、关键避坑:NIO和BIO适用场景速查

NIO不是“万能的”,找对场景才高效,对照下表直接用:

✅ 优先用NIO

  • 大文件/高并发文件操作(避免OOM)
  • 高并发网络编程(如网关、RPC,Netty基础)
  • 异步IO、跨平台路径操作

❌ 不适合用NIO

  • 小文件读写(如配置文件,BIO更简洁)
  • 低并发场景(BIO编程简单,易调试)

三、进阶实战:Files.lines() 处理大文件(告别OOM)

核心优势:懒加载模式,逐行读取释放内存,配合try-with-resources自动关闭资源,杜绝OOM!

3个常用场景,代码直接复制运行:

场景1:基础逐行读取

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class LargeFileStreamDemo {
    public static void main(String[] args) {
        try (Stream<String> lines = Files.lines(Paths.get("large_file.txt"))) {
            lines.forEach(line -> {
                // 替换为你的业务逻辑(如解析日志)
                System.out.println("处理行:" + line);
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

场景2:过滤统计(贴近业务)

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Optional;
import java.util.stream.Stream;

public class LargeFileAdvancedDemo {
    public static void main(String[] args) {
        try (Stream<String> lines = Files.lines(Paths.get("large_file.txt"))) {
            // 统计ERROR行数+找出第一行错误日志
            long errorCount = lines.filter(line -> line.contains("ERROR")).count();
            System.out.println("错误行总数:" + errorCount);

            // Stream只能消费一次,需重新获取
            try (Stream<String> lines2 = Files.lines(Paths.get("large_file.txt"))) {
                Optional<String> firstError = lines2.filter(line -> line.contains("ERROR")).findFirst();
                firstError.ifPresent(line -> System.out.println("第一行错误:" + line));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

场景3:超大文件并行处理(加速)

import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class LargeFileParallelDemo {
    public static void main(String[] args) {
        try (Stream<String> lines = Files.lines(Paths.get("super_large_file.txt"))) {
            // 并行流加速,统计WARN行数(保证业务线程安全)
            long warnCount = lines.parallel().filter(line -> line.startsWith("WARN")).count();
            System.out.println("警告行总数:" + warnCount);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

避坑提醒

  • Stream只能消费一次,多次处理需重新获取
  • 指定UTF-8编码(Files.lines(Paths.get(path), StandardCharsets.UTF_8)),避免乱码

四、正则实战:Pattern/Matcher 秒搞定文本处理

正则不用死记语法,掌握核心类+常用模板,80%场景直接套用。核心:Pattern(预编译模板)、Matcher(执行匹配)。

3个实战场景(代码可直接复制)

场景1:提取结构化数据(手机号、邮箱、URL)
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexExtractDemo {
    public static void main(String[] args) {
        String rawText = "联系:13812345678,邮箱:test@example.com,官网:https://www.xxx.com";
        // 提取手机号(11位,以1开头)
        Pattern phonePattern = Pattern.compile("1[3-9]\d{9}");
        Matcher phoneMatcher = phonePattern.matcher(rawText);
        while (phoneMatcher.find()) System.out.println("手机号:" + phoneMatcher.group());
        // 提取邮箱
        Pattern emailPattern = Pattern.compile("\w+@\w+\.\w+");
        Matcher emailMatcher = emailPattern.matcher(rawText);
        if (emailMatcher.find()) System.out.println("邮箱:" + emailMatcher.group());
        // 分组提取URL(协议+域名)
        Pattern urlPattern = Pattern.compile("(https?)://([a-zA-Z0-9.]+)");
        Matcher urlMatcher = urlPattern.matcher(rawText);
        if (urlMatcher.find()) {
            System.out.println("协议:" + urlMatcher.group(1) + ",域名:" + urlMatcher.group(2));
        }
    }
}
场景2:文本清洗(去除无用字符)
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexCleanDemo {
    public static void main(String[] args) {
        String dirtyText = "  2026-03-22 [ERROR] | 接口超时!\t错误码:500 ### 原因:超时  ###  ";
        // 步骤1:去除空白字符(空格、制表符)
        String step1 = Pattern.compile("\s+").matcher(dirtyText).replaceAll(" ");
        // 步骤2:去除###及前后空格
        String step2 = Pattern.compile("\s*###\s*").matcher(step1).replaceAll(" ");
        // 步骤3:标准化格式(去首尾空格+合并连续空格)
        String finalClean = step2.trim().replaceAll("\s+", " ");
        System.out.println("清洗后:" + finalClean);
    }
}
场景3:大文件正则清洗(NIO+正则结合)
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.regex.Pattern;
import java.util.stream.Stream;

public class LargeFileRegexClean {
    // 预编译正则(复用提升性能,别写循环里)
    private static final Pattern CLEAN_PATTERN = Pattern.compile("[^a-zA-Z0-9\u4e00-\u9fa5,:。!?\s]");

    public static void main(String[] args) {
        try (Stream<String> lines = Files.lines(Paths.get("dirty_large_file.txt"), StandardCharsets.UTF_8)) {
            Stream<String> cleanLines = lines
                    .map(line -> CLEAN_PATTERN.matcher(line).replaceAll("")) // 去除非法字符
                    .map(line -> line.trim().replaceAll("\s+", " ")) // 标准化
                    .filter(line -> !line.isEmpty()); // 过滤空行
            // 写入新文件
            Files.write(Paths.get("clean_large_file.txt"), (Iterable<String>) cleanLines::iterator, StandardCharsets.UTF_8);
            System.out.println("大文件清洗完成!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

常用正则模板(直接复用):

手机号:1[3-9]\d{9} | 邮箱:\w+@\w+\.(com|cn|net) | 中文:[\u4e00-\u9fa5] | 数字:\d+

正则避坑技巧

  1. Pattern预编译为静态常量,避免循环内重复编译,提升性能
  2. 分组捕获用(),group(0)是完整匹配,group(1)是第一个分组
  3. 贪婪匹配(.)vs 非贪婪匹配(.?),提取标签内容用非贪婪匹配

五、总结(速通重点)

  1. NIO核心:Path+Files+ByteBuffer+Channel,搞定文件IO所有痛点,避免OOM和跨平台报错
  2. 场景适配:大文件/高并发用NIO,小文件/低并发用BIO,不盲目追求“高效”
  3. 正则实战:Pattern预编译+Matcher匹配,掌握常用模板,文本处理秒速搞定
  4. 关键避坑:Files.lines()懒加载、Stream仅消费一次、正则预编译

技术在于实战,把代码复制下来动手敲一遍,很快就能掌握,看完就忘不如实操!