Конвертер CSV в HTML
Я написал небольшую программу, которая преобразует CSV-файл в HTML-таблицу. Это работает для моих целей. Но есть ли в моем коде части, которые можно было бы написать более чисто? Вы можете улучшить производительность? А могут быть ошибки? Я искал ошибки и, к счастью, не нашел.
Постскриптум
Возможно, мне следовало предоставить некоторую справочную информацию: я работаю над документацией по базе данных, которую пишу как документ HTML, потому что мне не нравятся документы Word. Однако создание табличного описания столбцов с десятками тегов затруднительно. Вот почему я написал этот скрипт: теперь мне нужно только экспортировать информацию о таблице как CSV и я могу преобразовать ее напрямую, не вводя много тегов самому. Вот почему нет тегов HTML и body: создаваемые таблицы должны быть не отдельными HTML-документами, а частями одного большого HTML-документа.
CsvToHtmlTable.java
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.FileWriter;
import java.util.List;
import java.util.ArrayList;
public class CsvToHtmlTable {
public static void main(String[] args) {
// print info and show user how to call the program if needed
System.out.println("This program is tested only for UTF-8 files.");
if (args[0].equalsIgnoreCase("help") || args[0].equalsIgnoreCase("-help") || args.length != 2) {
System.out.println("java CsvToHtmlTable <input file> <output file>");
System.out.println("Example: java CsvToHtmlTable nice.csv nice.html");
System.exit(0);
}
String csvFile = args[0];
String outputFile = args[1];
// read lines of csv to a string array list
List<String> lines = new ArrayList<String>();
try (BufferedReader reader = new BufferedReader(new FileReader(csvFile))) {
String currentLine;
while ((currentLine = reader.readLine()) != null) {
lines.add(currentLine);
}
} catch (IOException e) {
e.printStackTrace();
}
//embrace <td> and <tr> for lines and columns
for (int i = 0; i < lines.size(); i++) {
lines.set(i, "<tr><td>" + lines.get(i) + "</td></tr>");
lines.set(i, lines.get(i).replaceAll(",", "</td><td>"));
}
// embrace <table> and </table>
lines.set(0, "<table border>" + lines.get(0));
lines.set(lines.size() - 1, lines.get(lines.size() - 1) + "</table>");
// output result
try (FileWriter writer = new FileWriter(outputFile)) {
for (String line : lines) {
writer.write(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Как вызвать программу:
java CsvToHtmlTable ExampleInput.csv ExampleOutput.html
ExampleInput.csv
Name,Vorname,Alter
Ulbrecht,Klaus Dieter,12
Meier,Bertha,102
ExampleOutput.html
<table border><tr><td>Name</td><td>Vorname</td><td>Alter</td></tr>
<tr><td>Ulbrecht</td><td>Klaus Dieter</td><td>12</td></tr>
<tr><td>Meier</td><td>Bertha</td><td>102</td></tr></table>
Ответы
Хорошая реализация, найдите мои предложения в строке.
можно написать почище?
- В классе
java.nio.file.Filesесть несколько удобных методов, которые вы можете использовать:
lines = Files.readAllLines(Paths.get(csvFile), StandardCharsets.UTF_8);
//..
Files.write(Paths.get(outputFile), lines);
- Рассмотрите возможность создания константы для символа-разделителя. Некоторые файлы CSV разделены символом
;для обработки значений, содержащих запятые и т. Д .:
public static final String DELIMITER_CHAR=",";
- Предоставьте пользователю сообщение и выйдите в случае исключения ввода-вывода, например:
} catch (IOException e) {
System.out.println("Error reading input file: "+e.getMessage());
System.exit(1);
}
- Инкапсулируйте логику преобразования строк в метод, чтобы его было легче тестировать и повторно использовать. Например:
public class CsvToHtmlTable{
public static List<String> convert(List<String> lines){/**/}
public static void main(String[] args){/**/}
}
Вы можете улучшить производительность?
Предел этой реализации - ограничение памяти. Если входной файл большой, весь файл может не уместиться в доступной памяти.
Возможно, это не ваш случай, но если вам нужно обрабатывать большие файлы, подумайте о чтении и записи файла построчно.
А могут быть ошибки?
- Проверьте, пуст ли входной файл, иначе
lines.set(0,..)не работает - Как упоминалось в @ Doi9t, логика проверки ввода должна учитывать, когда нет аргументов, одного, двух или более.
CSVReader
Чтение CSV-файла может быть сложной задачей. Хотя многие файлы CSV представляют собой просто значения, разделенные запятыми, если значение содержит запятую, оно будет заключено в двойные кавычки, а если значение содержит двойные кавычки, сами двойные кавычки будут удвоены.
Чтобы обрабатывать не только базовые файлы CSV, но и библиотеку CSV, такую как OpenCSV (com.opencsv: opencsv: 5.0) или Apache Commons CSV (org.apache.commons: commons-csv: 1.7).
HTML
Действительный HTML
Ваш код просто пишет <table>...table data...</table>. Это неправильный HTML. Вам не хватает <html>...</html>тегов вокруг всего документа и <body>...</body>вокруг содержимого. Вам, вероятно, также следует иметь <head>...</head>, возможно, с хорошим <title>...</title>.
Побег
Если ваши данные CSV содержат какие-либо специальные символы, например <, >и &, вы действительно должны экранировать их в созданной таблице HTML.
Заголовки таблиц
Похоже, первая строка вашей таблицы содержит заголовки, а не данные. Вероятно, первую строку таблицы следует отформатировать <th>...</th>тегами вместо <td>...</td>тегов.
Линия за линией обработки
Вы читаете весь CSV-файл в память, и только когда он был полностью загружен, вы записываете его обратно как HTML. Это очень интенсивно использует память, особенно если файл CSV большой!
Вместо этого вы могли:
- открыть CSV
- откройте файл HTML
- написать пролог HTML
- для каждой строки, прочитанной из файла CSV:
- форматировать и записывать строку в HTML файл
- написать эпилог HTML
Непроверено, кодирование от бедра, без обработки цитирования в CSV или экранирования любых HTML-сущностей в выводе:
try (BufferedReader reader = new BufferedReader(new FileReader(csvFile));
FileWriter writer = new FileWriter(outputFile)) {
writer.write("<html><body><table border>\n");
String currentLine;
while ((currentLine = reader.readLine()) != null) {
writer.write("<tr>");
for(String field: currentLine.split(","))
writer.write("<td>" + field + "</td>");
writer.write("</tr>\n");
}
writer.write("</table></body></html>\n");
} catch (IOException e) {
e.printStackTrace();
}
XML и XSLT
Вы можете подумать о создании преобразователя CSV в XML.
Ваш XML-вывод может выглядеть так:
<data input-file='ExampleInput.csv'>
<person>
<Name>Ulbrecht</Name>
<Vorname>Klaus Dieter</Vorname>
<Alter>12</Alter>
</person>
<person>
<Name>Meier</Name>
<Vorname>Bertha</Vorname>
<Alter>102</Alter>
</person>
</data>
А затем вы можете использовать таблицу стилей XSLT для перевода XML в HTML, возможно, в браузере, даже не записывая HTML в файл.
Возможно NullPointerExceptionна линииif (args[0].equals...
Пожалуйста, проверьте argsразмер ввода, nullifyingпрежде чем переходить к любому индексу / элементу.
Да, это argsмогло быть nullвызвано другим загруженным классом. Или пустым, если пользователь забыл установить args.
Файл буферизации
Я думаю, что кеширование всего файла в памяти, а затем его обработка - это хорошая идея для вашего случая, поскольку каждую прочитанную вами строку вы можете просто обработать, записать и перейти к следующей строке. (как построчная обработка, упомянутая AJNeufeld)
Разорванный столбец (разделитель) Разделение
По сути, разделение данных столбцов с использованием не lines.get(i).replaceAll(",",...)работает, поскольку сами данные будут иметь ,содержание.
Считая строку Porsche,"991,991.2,992",70там, где ваш код (и даже тот, который предоставлен AJNeufeld) выйдет из строя, поскольку 991,991.2,992это одно значение, и ""они там, чтобы сообщить парсеру, экранированные данные впереди.
Итак, лично я предлагаю использовать процесс разбора символов, который позволяет вам пропустить любой ,разделитель, когда вы достигнете открытия "до его конечной "пары.
Неожиданные символы
Также рассматривается возможность утверждения, пропуска или преобразования любых неожиданных символов. Например, преобразовать символ NULL( \0) в 0x00или показать предупреждение и пропустить его.
Забыть файлы
Я предлагаю не блокировать ваше приложение, чтобы просто читать из файлов и сохранять в них. Много раз STDIN, и STDOUTэто более желанные способы.
Предлагаю поддерживать как из файла так и из файла STDIN. Например, используйте параметр must set -finдля ввода файла в аргументе или -stdinдля информирования инструмента, из которого выполняется чтение STDIN.
Проверка файлов
Проверка файлов (как входящих, так и исходящих) перед обработкой файла тоже будет отличной. Чтобы убедиться, что они доступны.
Утверждение
Очистка (или подсказка / настройка) результата при возникновении непредвиденного исключения ввода-вывода во время процесса тоже может быть хорошо.
Надеюсь, поможет.