Вопрос:
У меня есть сценарий, при котором мне приходится разбирать CSV файлы из разных источников, код синтаксического анализа очень прост и прост.
String csvFile = «/Users/csv/country.csv»; String line = «»; String cvsSplitBy = «,»; try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { while ((line = br.readLine()) != null) { // use comma as separator String[] country = line.split(cvsSplitBy); System.out.println(«Country [code= » + country[4] + » , name=» + country[5] + «]»); } } catch (IOException e) { e.printStackTrace(); }
моя проблема исходит от символа разделителя CSV, у меня много разных форматов, иногда это время , иногда это ;
существует ли какой-либо способ определить символ разделителя перед разбором файла
Ответ №1
univocity-parsers поддерживает автоматическое обнаружение разделителя (также строки окончаний и кавычек). Просто используйте его вместо борьбы с вашим кодом:
CsvParserSettings settings = new CsvParserSettings(); settings.detectFormatAutomatically(); CsvParser parser = new CsvParser(settings); List<String[]> rows = parser.parseAll(new File(«/path/to/your.csv»)); // if you want to see what it detected CsvFormat format = parser.getDetectedFormat();
Отказ от ответственности: я являюсь автором этой библиотеки, и я убедился, что всевозможные угловые случаи охвачены. Он с открытым исходным кодом и бесплатный (лицензия Apache 2.0)
Надеюсь это поможет.
Ответ №2
Да, но только если символы разделителя не могут существовать как обычный текст
Самый простой ответ – иметь список со всеми доступными символами-разделителями и попытаться определить, какой символ используется. Несмотря на это, вы должны установить некоторые ограничения на файлы или человека/людей, которые их создали. Посмотрите следующие два сценария:
Случай 1 – Содержание файла file.csv
test,test2,test3
Случай 2 – Содержание файла.csv
test1|test2,3|test4
Если у вас есть предварительное знание символов разделителя, тогда вы разделили бы первую строку , а вторую с помощью | , получая тот же результат. Но, если вы пытаетесь определить разделитель путем разбора файла, обе строки могут быть разделены с помощью , характер, и вы бы в конечном итоге с этим:
Случай 1 – результат использования split ,
test1 test2 test3
Случай 2 – Результат использования split ,
test1|test2 3|test4
Из-за отсутствия предварительного знания того, какой символ ограничителя используется, вы не можете создать “магический” алгоритм, который будет анализировать каждую комбинацию текста; даже регулярные выражения или подсчет числа появления символа не спасет вас.
Худший случай
test1,2|test3,4|test5
Просмотрев текст, его можно подделать, используя | как разделитель. Но частота появления обоих , и | одинаковы. Таким образом, с точки зрения алгоритма оба результата являются точными:
Правильный результат
test1,2 test3,4 test5
Неверный результат
test1 2|test3 4|test5
Если вы представляете набор рекомендаций или вы можете каким-то образом управлять генерацией файлов CSV, вы можете просто попытаться найти разделитель, используемый с String.contains(), используя вышеупомянутый список символов. Например:
public class MyClass { private List<String> delimiterList = new ArrayList<>(){{ add(«,»); add(«;»); add(«t»); // etc… }}; private static String determineDelimiter(String text) { for (String delimiter : delimiterList) { if(text.contains(delimiter)) { return delimiter; } } return «»; } public static void main(String[] args) { String csvFile = «/Users/csv/country.csv»; String line = «»; String cvsSplitBy = «,»; String delimiter = «»; boolean firstLine = true; try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { while ((line = br.readLine()) != null) { if(firstLine) { delimiter = determineDelimiter(line); if(delimiter.equalsIgnoreCase(«»)) { System.out.println(«Unsupported delimiter found: » + delimiter); return; } firstLine = false; } // use comma as separator String[] country = line.split(delimiter); System.out.println(«Country [code= » + country[4] + » , name=» + country[5] + «]»); } } catch (IOException e) { e.printStackTrace(); } } }
Обновить
Для более оптимизированного способа в методе defineDelimiter determineDelimiter() вместо цикла for-each вы можете использовать регулярные выражения.
Ответ №3
Если разделитель может отображаться в столбце данных, тогда вы просите о невозможности. Например, рассмотрим эту первую строку файла CSV:
one,two:three
Это может быть разделенный запятыми или разделенный двоеточием файл. Вы не можете определить, какой тип он есть.
Если вы можете гарантировать, что первая строка имеет все свои столбцы, окруженные кавычками, например, если всегда этот формат:
«one»,»two»,»three»
то вы можете использовать эту логику (хотя она не на 100% пуленепробиваемая):
if (line.contains(«»,»»)) delimiter = ‘,’; else if (line.contains(«»;»»)) delimiter = ‘;’;
Если вы не можете гарантировать такой ограниченный формат, то лучше передать символ разделителя в качестве параметра.
Затем вы можете прочитать файл, используя широко известный открытый CSV-анализатор, такой как Apache Commons CSV.
Ответ №4
Это зависит….
Если ваши наборы данных всегда имеют одинаковую длину и/или разделитель НИКОГДА не встречается в ваших столбцах данных, вы можете просто прочитать первую строку файла, посмотреть на него для longed для разделителя, установить его, а затем прочитать остальную часть файла, используя этот разделитель.
Что-то вроде
String csvFile = «/Users/csv/country.csv»; String line = «»; String cvsSplitBy = «,»; try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { while ((line = br.readLine()) != null) { // use comma as separator if (line.contains(«,»)) { cvsSplitBy = «,»; } else if (line.contains(«;»)) { cvsSplitBy = «;»; } else { System.out.println(«Wrong separator!»); } String[] country = line.split(cvsSplitBy); System.out.println(«Country [code= » + country[4] + » , name=» + country[5] + «]»); } } catch (IOException e) { e.printStackTrace(); }
Greetz Kai
Ответ №5
Добавьте условие, подобное этому,
String [] country; if(line.contains(«,») country = line.split(«,»); else if(line.contains(«;»)) country=line.split(«;»);