You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Batch:修改RegexLineTokenizer与ItemReader处理合并行数据

解决方案:在ItemReader层面拆分合并行,复用原有RegexLineTokenizer

针对你遇到的合并行问题,核心思路是在ItemReader读取行之后、交给Tokenizer解析之前,先拆分同一行中的多个带引号姓名条目,再将拆分后的单个条目逐个传递给原有的RegexLineTokenizer处理。这种方式不需要修改输入文件,也能复用你已有的正则和映射逻辑,同时适配大数据量场景(逐行处理,无内存溢出风险)。

实现步骤

1. 自定义包装类ItemReader

实现ItemStreamReader接口,内部包装原有的FlatFileItemReader,通过队列缓存拆分后的单行数据,每次read()调用返回一个拆分后的条目对应的Item。

import org.springframework.batch.item.ItemStreamReader;
import org.springframework.batch.item.ExecutionContext;
import org.springframework.batch.item.ItemStreamException;
import org.springframework.batch.item.file.FlatFileItemReader;
import java.util.LinkedList;
import java.util.Queue;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MultiLineSplittingItemReader<T> implements ItemStreamReader<T> {

    private final FlatFileItemReader<T> delegateReader;
    private final Queue<String> splitLinesCache = new LinkedList<>();
    // 匹配所有双引号包裹的姓名条目
    private final Pattern nameEntryPattern = Pattern.compile("\"([^\"]+)\"");

    public MultiLineSplittingItemReader(FlatFileItemReader<T> delegateReader) {
        this.delegateReader = delegateReader;
    }

    @Override
    public T read() throws Exception {
        // 优先从缓存队列取拆分后的单行
        if (!splitLinesCache.isEmpty()) {
            String singleLine = splitLinesCache.poll();
            return delegateReader.getLineMapper().mapLine(singleLine, delegateReader.getCurrentItemCount());
        }

        // 读取原始行
        String originalLine = delegateReader.readLine();
        if (originalLine == null) {
            return null;
        }

        // 拆分原始行中的多个姓名条目
        Matcher matcher = nameEntryPattern.matcher(originalLine);
        while (matcher.find()) {
            // 重新包装成带引号的单行格式,适配原有Tokenizer
            splitLinesCache.add("\"" + matcher.group(1) + "\"");
        }

        // 递归调用,返回第一个拆分后的条目
        return read();
    }

    @Override
    public void open(ExecutionContext executionContext) throws ItemStreamException {
        delegateReader.open(executionContext);
    }

    @Override
    public void update(ExecutionContext executionContext) throws ItemStreamException {
        delegateReader.update(executionContext);
    }

    @Override
    public void close() throws ItemStreamException {
        delegateReader.close();
        splitLinesCache.clear();
    }
}

2. 配置Spring Batch组件

复用你原有的RegexLineTokenizer和FieldSetMapper,仅替换ItemReader为自定义的包装类:

import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.builder.FlatFileItemReaderBuilder;
import org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper;
import org.springframework.batch.item.file.transform.RegexLineTokenizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;

@Configuration
public class BatchConfig {

    // 原有Tokenizer配置,无需修改
    @Bean
    public RegexLineTokenizer nameTokenizer() {
        RegexLineTokenizer tokenizer = new RegexLineTokenizer();
        tokenizer.setNames("firstName", "lastName");
        tokenizer.setRegex("(.*?)(?: .*)* (.*)");
        return tokenizer;
    }

    // 原有基础Reader配置,无需修改
    @Bean
    public FlatFileItemReader<Person> basePersonReader() {
        return new FlatFileItemReaderBuilder<Person>()
                .name("basePersonReader")
                .resource(new ClassPathResource("names.txt"))
                .lineTokenizer(nameTokenizer())
                .fieldSetMapper(new BeanWrapperFieldSetMapper<Person>() {{
                    setTargetType(Person.class);
                }})
                .build();
    }

    // 替换为自定义拆分Reader
    @Bean
    public ItemStreamReader<Person> splittingPersonReader() {
        return new MultiLineSplittingItemReader<>(basePersonReader());
    }
}

方案说明

  • 内存友好:逐行读取原始文件,拆分后的条目仅缓存少量在队列中,不会加载全量数据到内存,适配大数据量场景。
  • 复用原有逻辑:完全保留你已验证的正则表达式和字段映射规则,无需重新调试Tokenizer。
  • 无文件修改:直接读取原始输入文件,不需要提前预处理生成新文件。

内容的提问来源于stack exchange,提问作者Vamsi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 06:16:10