Konwerter CSV na HTML
Napisałem mały program, który konwertuje plik CSV na tabelę HTML. Działa na moje potrzeby. Ale czy są części w moim kodzie, które można napisać bardziej przejrzystym? Czy możesz poprawić wydajność? Czy są jakieś błędy? Szukałem błędów i na szczęście nie znalazłem.
Postscriptum
Może powinienem był podać kilka podstawowych informacji: pracuję nad dokumentacją bazy danych, którą piszę jako dokument HTML, ponieważ nie lubię dokumentów Worda. Jednak tworzenie tabelarycznego opisu kolumn z dziesiątkami tagów jest bolesne. Dlatego napisałem ten skrypt: Teraz muszę tylko wyeksportować informacje z tabeli jako CSV i mogę je bezpośrednio przekonwertować bez konieczności samodzielnego wprowadzania wielu tagów. Dlatego nie ma znaczników HTML i body: utworzone tabele nie powinny być oddzielnymi dokumentami HTML, ale częściami pojedynczego, dużego dokumentu 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();
}
}
}
Jak wywołać program:
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>
Odpowiedzi
Niezłe wdrożenie, moje sugestie są w tekście.
można napisać bardziej przejrzyście?
- Klasa
java.nio.file.Filesma kilka przydatnych metod, których możesz użyć:
lines = Files.readAllLines(Paths.get(csvFile), StandardCharsets.UTF_8);
//..
Files.write(Paths.get(outputFile), lines);
- Rozważ utworzenie stałej dla znaku ogranicznika. Niektóre pliki CSV są rozdzielane znakami,
;aby obsługiwać wartości zawierające przecinki itp .:
public static final String DELIMITER_CHAR=",";
- Podaj komunikat użytkownika i zakończ w przypadku wyjątku we / wy, na przykład:
} catch (IOException e) {
System.out.println("Error reading input file: "+e.getMessage());
System.exit(1);
}
- Hermetyzuj logikę, aby przekonwertować linie na metodę, aby łatwiej było testować i ponownie używać. Na przykład:
public class CsvToHtmlTable{
public static List<String> convert(List<String> lines){/**/}
public static void main(String[] args){/**/}
}
Czy możesz poprawić wydajność?
Ograniczeniem tej implementacji jest ograniczenie pamięci. Jeśli plik wejściowy jest duży, cały plik może nie zmieścić się w dostępnej pamięci.
Może nie w Twoim przypadku, ale jeśli potrzebujesz obsługiwać duże pliki, rozważ odczyt i zapis pliku wiersz po wierszu.
Czy są jakieś błędy?
- Sprawdź, czy plik wejściowy jest pusty, w przeciwnym razie
lines.set(0,..)nie powiedzie się - Jak wspomniał @ Doi9t, logika walidacji danych wejściowych musi zostać rozważona, gdy nie ma żadnych argumentów, jeden, dwa lub więcej.
CSVReader
Czytanie pliku CSV może być złożonym zadaniem. Chociaż wiele plików CSV to tylko wartości rozdzielane przecinkami, jeśli wartość zawiera przecinek, zostanie ujęta w podwójne cudzysłowy, a jeśli wartość zawiera cudzysłowy, same cudzysłowy są podwajane.
Aby poradzić sobie z tym czymś więcej niż zwykłymi plikami CSV, naprawdę powinieneś użyć biblioteki CSV, takiej jak OpenCSV (com.opencsv: opencsv: 5.0) lub Apache Commons CSV (org.apache.commons: commons-csv: 1.7).
HTML
Prawidłowy HTML
Twój kod po prostu pisze <table>...table data...</table>. To nie jest poprawny HTML. Brakuje <html>...</html>tagów w całym dokumencie i <body>...</body>wokół treści. Prawdopodobnie powinieneś również mieć <head>...</head>, być może z ładnym <title>...</title>.
Ucieczka
Jeśli dane CSV zawiera żadnych znaków specjalnych, takich jak <, >i &, naprawdę musi uciec je w wygenerowanym tabeli HTML.
Nagłówki tabeli
Wygląda na to, że pierwsza linia tabeli zawiera nagłówki, a nie dane. Pierwszy wiersz tabeli powinien być prawdopodobnie sformatowany za pomocą <th>...</th>tagów zamiast <td>...</td>tagów.
Przetwarzanie linia po linii
Wczytujesz cały plik CSV do pamięci i dopiero po załadowaniu go w całości zapisujesz go z powrotem jako HTML. Zajmuje to bardzo dużo pamięci, zwłaszcza jeśli plik CSV jest ogromny!
Zamiast tego możesz:
- otwórz plik CSV
- otwórz plik HTML
- napisz prolog HTML
- dla każdego wiersza odczytanego z pliku CSV:
- sformatuj i zapisz wiersz do pliku HTML
- napisz epilog HTML
Nietestowane, kodowanie z biodra, bez obsługi cytowania w CSV lub ucieczki jakichkolwiek encji HTML w wyniku:
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 i XSLT
Możesz rozważyć utworzenie translatora CSV na XML.
Twoje wyjście XML może wyglądać następująco:
<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>
Następnie możesz użyć arkusza stylów XSLT, aby przetłumaczyć XML na HTML, prawdopodobnie w przeglądarce bez zapisywania kodu HTML do pliku.
Możliwe NullPointerExceptionna liniiif (args[0].equals...
Sprawdź argsrozmiar wejściowy i nullifyingzanim przejdziesz do jakiegokolwiek indeksu / pozycji.
Tak, to argsmoże być nullwywołane przez inną załadowaną klasę. Lub pusty, jeśli użytkownik zapomni ustawić args.
Buforowanie pliku
Myślę, że buforowanie całego pliku w pamięci, a następnie przetwarzanie go jest dobrym pomysłem dla twojego przypadku, ponieważ każda przeczytana linia może po prostu przetworzyć, zapisać i przejść do następnej linii. (zgodnie z przetwarzaniem Line By Line wspomnianym przez AJNeufeld)
Podział łamanej kolumny (separator)
Zasadniczo dzielenie kolumn za pomocą danych lines.get(i).replaceAll(",",...)jest zepsute, ponieważ same dane miałyby ,zawartość.
Biorąc pod uwagę wiersz jako miejsce, w Porsche,"991,991.2,992",70którym twój kod (a nawet ten dostarczony przez AJNeufeld) zawiedzie, ponieważ 991,991.2,992ma jedną wartość, a te ""są po to, aby powiedzieć parserowi, dane ucieczki są przed nami.
Osobiście radzę więc zastosować proces parsowania znak po znaku, który pozwala pominąć dowolny ,separator, gdy dojdziesz do otwarcia "aż do "pary końcowej .
Nieoczekiwane znaki
Rozważ także potwierdzenie, pominięcie lub przekonwertowanie nieoczekiwanych znaków. Na przykład konwersja znaku NULL( \0) na 0x00lub wyświetlenie ostrzeżenia i pominięcie go.
Zapomnij o plikach
Sugeruję, aby nie blokować aplikacji, aby po prostu czytać z plików i zapisywać w nich. Wiele razy STDINi STDOUTsą bardziej pożądane sposoby.
Sugeruję wsparcie zarówno z pliku, jak i STDIN. Na przykład, użyj muszą ustawić -finplik wejściowy w argumencie lub -stdinpoinformować narzędzie, z którego czyta STDIN.
Sprawdzanie plików
Sprawdzanie plików (zarówno do wewnątrz, jak i na zewnątrz) przed przetworzeniem pliku również będzie świetne. Aby upewnić się, że są dostępne.
Twierdzenie
Oczyszczanie (lub monitowanie / ustawianie) wyniku, podczas gdy występuje nieoczekiwany wyjątek we / wy podczas procesu, również może być dobre.
Mam nadzieję, że to pomoże.