CSVからHTMLへのコンバーター
CSVファイルをHTMLテーブルに変換する小さなプログラムを作成しました。それは私の目的のために働きます。しかし、私のコードには、よりクリーンに記述できる部分がありますか?多分パフォーマンスを改善できますか?バグはありますか?バグを探しましたが、幸いなことに見つかりませんでした。
追記
たぶん私はいくつかの背景情報を提供するべきでした:私はWord文書が好きではないので、HTML文書として書いているデータベース文書に取り組んでいます。ただし、数十のタグを含む列の表形式の説明を作成するのは面倒です。そのため、このスクリプトを作成しました。テーブル情報を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=",";
- I / O例外が発生した場合は、ユーザーメッセージを入力して終了します。次に例を示します。
} 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){/**/}
}
多分パフォーマンスを改善できますか?
この実装の制限は、メモリの制約です。入力ファイルが大きい場合、ファイル全体が使用可能なメモリに収まらない可能性があります。
あなたの場合ではないかもしれませんが、大きなファイルを処理する必要がある場合は、ファイルを1行ずつ読み書きすることを検討してください。
バグはありますか?
- 入力ファイルが空かどうかを確認してください。空でない場合は
lines.set(0,..)失敗します - @ Doi9tが述べたように、入力検証ロジックは、引数が1つ、2つ、またはそれ以上ない場合に考慮する必要があります。
CSVReader
CSVファイルの読み取りは複雑な作業になる可能性があります。多くのCSVファイルはカンマ区切りの値ですが、値にカンマが含まれている場合は二重引用符で囲まれ、値に二重引用符が含まれている場合は二重引用符自体が二重になります。
これらを基本的なCSVファイルだけでなく処理するには、OpenCSV(com.opencsv:opencsv:5.0)やApache Commons CSV(org.apache.commons:commons-csv:1.7)などのCSVライブラリを使用する必要があります。
HTML
有効なHTML
あなたのコードは本質的にただ書くだけ<table>...table data...</table>です。これは適切なHTMLではありません。<html>...</html>ドキュメント全体と<body>...</body>コンテンツの周りにタグがありません。あなたはおそらくまた<head>...</head>、おそらく素敵なを持っている必要があります<title>...</title>。
脱出
あなたのCSVデータに特殊文字が含まれている場合は、のように<、>と&、あなたは本当に、生成されたHTMLテーブルでそれらをエスケープする必要があります。
表の見出し
テーブルの最初の行には、データではなく見出しが含まれているようです。最初のテーブル行は、おそらく<th>...</th>タグではなくタグでフォーマットする必要があり<td>...</td>ます。
行ごとの処理
CSVファイル全体をメモリに読み込んでおり、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が言及したLine By Line Processingとして)
壊れた列(区切り文字)の分割
基本的に、lines.get(i).replaceAll(",",...)データ自体が,コンテンツとして含まれるため、を使用して列データを分割することはできません。
Porsche,"991,991.2,992",70コード(およびAJNeufeldによって提供されたもの)が失敗する行を考慮します。これ991,991.2,992は1つの値であり""、パーサーに通知するために存在するため、エスケープされたデータが先行します。
だから、個人的に、私はあなたがいずれかをスキップできるようにという、文字ごとの文字は解析処理のために行くお勧めし,ますが、開口部に達したときに区切り文字として"そのエンディングゴマ"のペアを。
予期しない文字
また、予期しない文字をアサート、スキップ、または変換することも検討しています。たとえば、NULL(\0)文字を0x00警告に変換するか、警告を表示してスキップします。
ファイルを忘れる
アプリをロックしてファイルから読み取るだけでなく、ファイルに保存することをお勧めします。多くの場合STDIN、そしてSTDOUTより歓迎される方法です。
ファイルとの両方からサポートすることをお勧めしますSTDIN。たとえば-fin、引数にfile-inputを設定するか-stdin、から読み取ったツールに通知する必要がありますSTDIN。
ファイルの確認
ファイルを処理する前にファイル(イン/アウトの両方)をチェックすることも素晴らしいでしょう。それらがアクセス可能であることを確認するため。
アサーション
結果をクリーンアップ(またはプロンプト/設定可能)しますが、プロセス中に予期しないIO例外が発生することもあります。
それが役に立てば幸い。