Как пропустить только пробелы и строки, имеющие переменные столбцы, используя supercsv

Я работаю над требованием парсера CSV и использую библиотеку парсера supercsv. Мой CSV-файл может иметь 25 столбцов (разделенных табуляцией (|)) и до 100 тыс. Строк с дополнительной строкой заголовка.

Я хотел бы игнорировать только пробелы и строки, содержащие менее 25 столбцов.

Я использую IcvBeanReader с сопоставлениями имен (для установки значений CSV в Pojo) и обработчиками полей (для обработки проверок) для чтения файла.

Я предполагаю, что Supercsv IcvBeanReader пропустит пустые строки по умолчанию. Но как поступить, если строка содержит менее 25 номеров столбцов?

2 ответа

Решение

Вы можете легко сделать это, написав свой собственный токенизатор.

Например, следующий 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;
    }
}

И простой тест с использованием этого токенизатора...

@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]

(1) Если выбор должен быть сделан вашей Java-программой, используя Super CSVтогда (и я цитирую) "вам придется использовать CsvListReader". В частности: listReader.length()

Смотрите эту страницу Super CSV для деталей.

(2) Если вы можете выполнить выбор, предварительно обработав файл CSV, то вы можете рассмотреть подходящий инструмент командной строки (или инструменты, в зависимости от того, насколько сложен формат CSV). Если разделитель файла CSV не встречается ни в одном поле, тогда достаточно awk. Например, если предположение выполнено, и если разделитель |тогда соответствующий awk-фильтр может быть таким простым:

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

Если формат файла CSV слишком сложен для наивного применения awk, вы можете преобразовать сложный формат в более простой; часто TSV может многое порекомендовать.

Другие вопросы по тегам