Парсер Apache CSV не работает с данными, разделенными табуляцией, с кавычками
Я хочу проанализировать отчет о транзакциях в электронной книге Google. Я открыл его в Notepad++, чтобы увидеть точно разделенные поля и записи. Это файл с разделителями табуляции, каждое поле заголовка и поле данных заключено в кавычки. Первые две строки файла CSV:
"Дата транзакции", "Идентификатор", "Продукт", "Тип", "Предварительный заказ", "Кол-во", "Основной номер ISBN", "Название оттиска", "Заголовок", "Автор", "Валюта исходной цены", "Валюта исходной цены", "Валюта ценовой категории", Прайс-лист [с учетом налогов]" " Прайс-лист [без НДС]" " Страна продажи "" Доход издателя%" " Доход издателя "" Валюта платежа "" Сумма платежа "" Курс конвертации валют " "2016. 09. 01." "ID:1166315449551685" "Одиночная покупка" "Продажа" "Нет" "1" "9789633780664" "Book and Walk Kft" "Bánk bán" "József Katona" "HUF" "0,00" "HUF" "0,00" "0,00" "HU" "52,0%" "0,00" "" "" ""
Я использую следующий код для разбора файла CSV:
private List<Sales> parseCsv(File csv) {
Calendar max = Calendar.getInstance();
Calendar current = Calendar.getInstance();
boolean firstRound = true;
List<Sales> sales = new ArrayList<>();
Sales currentRecord;
Reader in;
try {
in = new FileReader(csv);
Iterable<CSVRecord> records;
try {
records = CSVFormat.TDF.withQuote('\"').withFirstRecordAsHeader().parse(in);
for (CSVRecord record : records) {
currentRecord = new Sales();
currentRecord.setAuthor(record.get("Author"));
currentRecord.setTitle(record.get("Title"));
currentRecord.setPublisher(record.get("Imprint Name"));
currentRecord.setIsbn(record.get("Primary ISBN"));
currentRecord.setChannel("Google");
currentRecord.setBookId(record.get("Id"));
currentRecord.setCountry(record.get("Country of Sale"));
currentRecord.setUnits(Integer.parseInt(record.get("Qty")));
currentRecord.setUnitPrice(Float.parseFloat(record.get("List Price [tax exclusive]")));
Date transDate;
try {
transDate = sourceDateFormat.parse(record.get("Transaction Date"));
if (firstRound) {
max.setTime(transDate);
};
current.setTime(transDate);
if (current.after(max)) {
max.setTime(current.getTime());
}
currentRecord.setDatum(transDate);
} catch (ParseException e) {
// TODO Auto-generated catch block
LOG.log(Level.SEVERE,"Nem megfeelő formátumú a dátum a {0} file-ban",csv.getAbsolutePath());
}
currentRecord.setCurrencyCustomer(record.get("List Price Currency"));
currentRecord.setCurrencyProceeds(record.get("Payment Amount"));
currentRecord.setCurrencyProceeds(record.get("Payment Currency"));
sales.add(currentRecord);
}
LOG.log(Level.INFO, "Daily sales transactions of {0} were successfully parsed from ",
csv.getAbsolutePath());
return sales;
} catch (IOException e1) {
// TODO Auto-generated catch block
LOG.log(Level.SEVERE, "Valami nem stimmel a {0} file szerkezetével",csv.getAbsolutePath());
}
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
LOG.log(Level.SEVERE,"A {0} file-t nem találom.",csv.getAbsolutePath());
}
return null;
};
Когда я отлаживаю процесс разбора, я вижу, что record.get("Author") выдал исключение времени выполнения:
java.lang.IllegalArgumentException: Mapping for Author not found, expected one of [��"
Очевидно, у меня есть столбец с именем Автор. Любая идея, что идет не так?
2 ответа
При преобразовании этого модуля в модульный тест и запуске с текущей версией commons-csv 1.4 это прекрасно работает для меня, поэтому:
- Проверьте с последней версией commons-csv
- Убедитесь, что на самом деле в файле есть табуляции, а не заглушки вокруг записей автора по какой-то причине.
- Укажите фактическую кодировку файла при вызове parse() для правильной обработки не-ASCII символов (благодаря комментариям от @tonakai)
Следующий юнит-тест прекрасно работает с commons-csv 1.4
private final static String DATA = "\"Transaction Date\"\t\"Id\"\t\"Product\"\t\"Type\"\t\"Preorder\"\t\"Qty\"\t\"Primary ISBN\"\t\"Imprint Name\"\t\"Title\"\t\"Author\"\t\"Original List Price Currency\"\t\"Original List Price\"\t\"List Price Currency\"\t\"List Price [tax inclusive]\"\t\"List Price [tax exclusive]\"\t\"Country of Sale\"\t\"Publisher Revenue %\"\t\"Publisher Revenue\"\t\"Payment Currency\"\t\"Payment Amount\"\t\"Currency Conversion Rate\"\n" +
"\"2016. 09. 01.\"\t\"ID:1166315449551685\"\t\"Single Purchase\"\t\"Sale\"\t\"None\"\t\"1\"\t\"9789633780664\"\t\"Book and Walk Kft\"\t\"Bánk bán\"\t\"József Katona\"\t\"HUF\"\t\"0,00\"\t\"HUF\"\t\"0,00\"\t\"0,00\"\t\"HU\"\t\"52,0%\"\t\"0,00\"\t\"\"\t\"\"\t\"\"";
@Test
public void parseCsv() throws IOException {
final CSVFormat format = CSVFormat.TDF.withQuote('\"').withFirstRecordAsHeader();
Iterable<CSVRecord> records = format.parse(new StringReader(DATA));
System.out.println("Headers: " + Arrays.toString(format.getHeader()));
for (CSVRecord record : records) {
assertNotNull(record.get("Author"));
assertNotNull(record.get("Title"));
assertNotNull(record.get("Imprint Name"));
assertNotNull(record.get("Primary ISBN"));
assertNotNull(record.get("Id"));
assertNotNull(record.get("Country of Sale"));
assertNotNull(record.get("Qty"));
assertNotNull(record.get("List Price [tax exclusive]"));
assertNotNull(record.get("Transaction Date"));
assertNotNull(record.get("List Price Currency"));
assertNotNull(record.get("Payment Amount"));
assertNotNull(record.get("Payment Currency"));
System.out.println("Record: " + record.toString());
}
}
Оказалось, что источником проблемы была кодировка. Основываясь на комментарии @tonakai, я начал анализировать кодировку отчета Google CSV. Это был UTF-16 Little Endian. Поскольку мой файл содержал метку порядка байтов, мне пришлось использовать 'BOMInputStream' и немного реорганизовать мой код. 'Reader r = newReader(csv); CSVParser csvParser= CSVFormat.TDF.withFirstRecordAsHeader(). WithQuoteMode (QuoteMode.ALL).parse (r);.....
private InputStreamReader newReader (конечный файл csv) выдает FileNotFoundException {возвращать новый InputStreamReader(новый BOMInputStream(новый FileInputStream(csv),ByteOrderMark.UTF_16LE), StandardCharsets.UTF_16LE); }'Это работает сейчас