Разбор ошибок из-за различий CSV до / после сохранения (Java с Apache Commons CSV)

У меня есть файл CSV с 37 столбцами, который я анализирую на Java с помощью Apache Commons CSV 1.2. Мой установочный код выглядит следующим образом:

//initialize FileReader object
FileReader fileReader = new FileReader(file);

//intialize CSVFormat object
CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER_MAPPING);

//initialize CSVParser object
CSVParser csvFileParser = new CSVParser(fileReader, csvFileFormat);

//Get a list of CSV file records
List<CSVRecord> csvRecords = csvFileParser.getRecords();

// process accordingly

Моя проблема в том, что, когда я копирую CSV для обработки в целевой каталог и запускаю программу синтаксического анализа, я получаю следующую ошибку:

Exception in thread "main" java.lang.IllegalArgumentException: Index for header 'Title' is 7 but CSVRecord only has 6 values!
        at org.apache.commons.csv.CSVRecord.get(CSVRecord.java:110)
        at launcher.QualysImport.createQualysRecords(Unknown Source)
        at launcher.QualysImport.importQualysRecords(Unknown Source)
        at launcher.Main.main(Unknown Source)

Однако, если я скопирую файл в целевой каталог, открою и сохраню его, а затем снова попробую программу, она заработает. Открытие и сохранение файла CSV добавляет запятые, необходимые в конце, поэтому моя программа не скомпрометирует отсутствие достаточного количества заголовков для чтения.

Для контекста, вот пример строки до / после сохранения:

До (сбой): "данные", "данные", "данные", "данные"

После (работы): "данные", "данные",,,, "данные",,, "данные",,,,,,

Итак, мой вопрос: почему формат CSV меняется, когда я открываю и сохраняю его? Я не изменяю никакие значения или кодировку, и поведение сохраняется для MS-DOS или обычного формата.csv при сохранении. Кроме того, я использую Excel для копирования / открытия / сохранения в моем тестировании.

Нужно ли использовать кодировку или формат? Могу ли я решить это программно?

Заранее спасибо!

РЕДАКТИРОВАНИЕ № 1:

Для дополнительного контекста, когда я впервые просматриваю пустую строку в исходном файле, у него просто есть новая строка ^M, например:

^M

После открытия в Excel и сохранения это выглядит так со всеми 37 моими пустыми полями:

,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,^M

Это несоответствие кодировки Windows?

3 ответа

Решение

Может быть, это проблема совместимости с тем, что изначально создавало файл. Похоже, что Excel принимает пустую строку в качестве допустимой строки с пустыми строками в каждом столбце, причем количество столбцов совпадает с некоторыми другими строками. Затем он сохраняет его в соответствии с соглашениями CSV с разделителем столбцов. (^M - это символ возврата каретки; в системах Microsoft он предшествует символу перевода строки в конце строки в текстовых файлах)

Возможно, вы можете справиться с этим, создав собственный подкласс Reader, который будет находиться между FileReader и CSVParser. Ваш читатель прочтет строку, и если она не заполнена, верните строку с правильным количеством запятых. В противном случае просто верните строку как есть.

Например:

class MyCSVCompatibilityReader extends BufferedReader
    {
    private final BufferedReader delegate;

    public MyCSVCompatibilityReader(final FileReader fileReader)
        {
        this.delegate = new BufferedReader(fileReader);
        }

    @Override
    public String readLine()
        {
        final String line = this.delegate.readLine();
        if ("".equals(line.trim())
            { return ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"; }
        else
            { return line; }
        }
    }

Есть много других деталей, которые нужно правильно реализовать при реализации интерфейса. Вам нужно будет пройти через вызовы ко всем другим методам (закрыть, подготовить, сбросить, пропустить и т. Д.) И убедиться, что каждый из различных read() методы работают правильно. Может быть проще, если файл легко помещается в памяти, просто прочитать файл и записать фиксированную версию в новый StringWriter, а затем создать StringReader для CSVParser.

Или, может быть, попробовать с AllowMissingColumnNames?

//intialize CSVFormat object 
CSVFormat csvFileFormat = CSVFormat.DEFAULT.withHeader(FILE_HEADER_MAPPING).withAllowMissingColumnNames();

Возможно попробуйте это: Создает парсер для данного файла. синтаксический анализ (файл, кодировка Charset, формат CSVFormat)

// импорт импорт java.nio.charset.StandardCharsets; //StandardCharsets.UTF_8

Примечание. Этот метод внутренне создает FileReader с использованием FileReader.FileReader(java.io.File), который, в свою очередь, опирается на кодировку по умолчанию для JVM, которая выполняет код.

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