【问题标题】:How do I skip white-space only lines and lines having variable columns using supercsv如何使用 supercsv 跳过仅空白行和具有可变列的行
【发布时间】:2016-01-23 01:54:02
【问题描述】:

我正在研究 CSV 解析器要求,并且我正在使用 supercsv 解析器库。我的 CSV 文件可以有 25 列(由制表符 (|) 分隔)和多达 100k 行以及额外的标题行。

我想忽略只有空格的行和包含少于 25 列的行。

我正在使用 IcvBeanReader 和名称映射(将 csv 值设置为 pojo)和字段处理器(处理验证)来读取文件。

我假设 Supercsv IcvBeanReader 默认会跳过空白行。但是如果一行包含少于25个列号怎么处理呢?

【问题讨论】:

  • 什么是十万?为什么你写的是制表符而不是竖线字符(|)?
  • 这看起来像this question 的更简单版本,但不是读取包含错误列的行,而是忽略它们

标签: java csv opencsv supercsv


【解决方案1】:

您可以通过编写自己的 Tokenizer 轻松做到这一点。

例如,以下 Tokenizer 将具有与默认行为相同的行为,但会跳过所有列数不正确的行。

public class SkipBadColumnCountTokenizer extends Tokenizer {

    private final int expectedColumns;

    private final List<Integer> ignoredLines = new ArrayList<>();

    public SkipBadColumnCountTokenizer(Reader reader, 
            CsvPreference preferences, int expectedColumns) {
        super(reader, preferences);
        this.expectedColumns = expectedColumns;
    }

    @Override
    public boolean readColumns(List<String> columns) throws IOException {
        boolean moreInputExists;
        while ((moreInputExists = super.readColumns(columns)) && 
            columns.size() != this.expectedColumns){
            System.out.println(String.format("Ignoring line %s with %d columns: %s", getLineNumber(), columns.size(), getUntokenizedRow()));
            ignoredLines.add(getLineNumber());
        }

        return moreInputExists;

    }

    public List<Integer> getIgnoredLines(){
        return this.ignoredLines;
    }
}

还有一个使用这个 Tokenizer 的简单测试...

@Test
public void testInvalidRows() throws IOException {

    String input = "column1,column2,column3\n" +
            "has,three,columns\n" +
            "only,two\n" +
            "one\n" +
            "three,columns,again\n" +
            "one,too,many,columns";

    CsvPreference preference = CsvPreference.EXCEL_PREFERENCE;
    int expectedColumns = 3;
    SkipBadColumnCountTokenizer tokenizer = new SkipBadColumnCountTokenizer(
        new StringReader(input), preference, expectedColumns);

    try (ICsvBeanReader beanReader = new CsvBeanReader(tokenizer, preference)) {
        String[] header = beanReader.getHeader(true);
        TestBean bean;
        while ((bean = beanReader.read(TestBean.class, header)) != null){
            System.out.println(bean);
        }
        System.out.println(String.format("Ignored lines: %s", tokenizer.getIgnoredLines()));
    }

}

打印以下输出(注意它是如何跳过所有无效行的):

TestBean{column1='has', column2='three', column3='columns'}
Ignoring line 3 with 2 columns: only,two
Ignoring line 4 with 1 columns: one
TestBean{column1='three', column2='columns', column3='again'}
Ignoring line 6 with 4 columns: one,too,many,columns
Ignored lines: [3, 4, 6]

【讨论】:

  • 非常感谢。它工作正常 int expectedColumns = 25; Tokenizer tokenizer = new SkipBadColumnCountTokenizer(new FileReader(file), new CsvPreference.Builder('"', '|', "\n").build(), expectedColumns); beanReader = new CsvBeanReader(tokenizer, new CsvPreference.Builder( '"', '|', "\n").build());有什么方法可以捕获包含坏列的行号?
  • 当然,您可以在 Tokenizer 中调用 getLineNumber() - 将其记录或收集到列表中并在最后检索它。我已经更新了上面的示例来演示日志记录。
  • 再次感谢。此方法返回布尔值及其覆盖的 Tokenizer 方法。您能否帮助如何将这些坏列记录号捕获到 List 中。我需要在 TestInvalidRows(您在上面写过)类级别拥有该列表。我再次为您的支持感到非常高兴,因为在其他帖子中查看了您的自定义 CSV 异常处理程序之后..我设法处理字段级验证错误而不会引发异常。
  • 不用担心,我刚刚更新了示例 Tokenizer 以演示一种捕获行号的方法。
  • 非常感谢您对此的支持 :)
【解决方案2】:

(1) 如果选择必须由您的 Java 程序使用Super CSV 完成,那么(我引用)“您将不得不使用 CsvListReader”。特别是:listReader.length()

详情请见this Super CSV page

(2) 如果您可以通过预处理 CSV 文件来执行选择,那么您可能希望考虑使用合适的命令行工具(或工具,具体取决于 CSV 格式的复杂程度)。如果 CSV 文件的分隔符没有出现在任何字段中,那么 awk 就足够了。例如,如果满足假设,并且分隔符是|,那么相关的 awk 过滤器可以很简单:

awk -F'|' 'NF == 25 {print}'

如果 CSV 文件格式对于 awk 的幼稚应用来说过于复杂,那么您可能希望将复杂格式转换为更简单的格式;通常 TSV 有很多值得推荐的地方。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-27
    相关资源
    最近更新 更多