Công cụ chuyển đổi CSV sang HTML

Sep 08 2020

Tôi đã viết một chương trình nhỏ chuyển đổi tệp CSV sang bảng HTML. Nó hoạt động cho các mục đích của tôi. Nhưng có những phần nào trong mã của tôi có thể được viết rõ ràng hơn không? Bạn có thể cải thiện hiệu suất có thể không? Có thể có bất kỳ lỗi? Tôi đã tìm kiếm lỗi và may mắn thay tôi đã không tìm thấy một số.

Tái bút

Có lẽ tôi nên cung cấp một số thông tin cơ bản: Tôi đang làm việc trên một tài liệu cơ sở dữ liệu mà tôi đang viết dưới dạng tài liệu HTML, bởi vì tôi không thích tài liệu Word. Tuy nhiên, việc tạo mô tả dạng bảng của các cột với hàng chục thẻ là một việc khó khăn. Đó là lý do tại sao tôi viết tập lệnh này: Bây giờ tôi chỉ phải xuất thông tin bảng dưới dạng CSV và có thể chuyển đổi trực tiếp mà không cần phải tự nhập nhiều thẻ. Đây là lý do tại sao không có HTML và thẻ nội dung: Các bảng được tạo không nên là các tài liệu HTML riêng biệt, mà là các phần của một tài liệu HTML lớn, duy nhất.

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();
        }
    }
}

Cách gọi chương trình:

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>

Trả lời

4 Marc Sep 09 2020 at 03:25

Triển khai tốt, tìm thấy các đề xuất của tôi nội tuyến.


có thể được viết sạch hơn?

  • Lớp java.nio.file.Filescó một số phương thức hữu ích mà bạn có thể sử dụng:
lines = Files.readAllLines(Paths.get(csvFile), StandardCharsets.UTF_8);
//..
Files.write(Paths.get(outputFile), lines);
  • Hãy xem xét để tạo một hằng số cho ký tự phân cách. Một số tệp CSV được phân cách bằng ;để xử lý các giá trị có chứa dấu phẩy, v.v.:
public static final String DELIMITER_CHAR=",";
  • Cung cấp thông báo người dùng và thoát trong trường hợp I / O Exception, ví dụ:
} catch (IOException e) {
    System.out.println("Error reading input file: "+e.getMessage());
    System.exit(1);
}
  • Đóng gói logic để chuyển đổi các dòng thành một phương thức, để việc kiểm tra và sử dụng lại dễ dàng hơn. Ví dụ:
public class CsvToHtmlTable{
    public static List<String> convert(List<String> lines){/**/}
    public static void main(String[] args){/**/}
}

Bạn có thể cải thiện hiệu suất có thể không?

Giới hạn của việc triển khai này là giới hạn bộ nhớ. Nếu tệp đầu vào lớn, toàn bộ tệp có thể không vừa trong bộ nhớ khả dụng.

Có thể không phải là trường hợp của bạn, nhưng nếu bạn cần xử lý các tệp lớn, hãy xem xét việc đọc và ghi từng dòng tệp.


Có thể có bất kỳ lỗi?

  • Kiểm tra xem tệp đầu vào có trống không, nếu lines.set(0,..)không sẽ không thành công
  • Như @ Doi9t đã đề cập, logic xác thực đầu vào cần phải xem xét khi không có đối số, một, hai hoặc nhiều hơn.
5 AJNeufeld Sep 09 2020 at 03:02

CSVReader

Đọc tệp CSV có thể là một nhiệm vụ phức tạp. Mặc dù nhiều tệp CSV chỉ là các giá trị được phân tách bằng dấu phẩy, nhưng nếu một giá trị chứa dấu phẩy, nó sẽ được bao quanh bởi dấu ngoặc kép và nếu giá trị chứa dấu ngoặc kép thì bản thân dấu ngoặc kép sẽ được nhân đôi.

Để xử lý những vấn đề này chỉ là tệp CSV cơ bản, bạn thực sự nên sử dụng thư viện CSV, chẳng hạn như OpenCSV (com.opencsv: opencsv: 5.0) hoặc Apache Commons CSV (org.apache.commons: commons-csv: 1.7).

HTML

HTML hợp lệ

Mã của bạn về cơ bản chỉ viết <table>...table data...</table>. Đây không phải là HTML thích hợp. Bạn đang thiếu <html>...</html>các thẻ xung quanh toàn bộ tài liệu và <body>...</body>xung quanh nội dung. Bạn có lẽ cũng nên có một <head>...</head>, có lẽ với một tốt đẹp <title>...</title>.

Bỏ trốn

Nếu dữ liệu CSV của bạn có chứa bất kỳ ký tự đặc biệt, giống như <, >và &, bạn thực sự phải thoát khỏi chúng trong bảng HTML được tạo.

Tiêu đề bảng

Có vẻ như dòng đầu tiên trong bảng của bạn chứa các tiêu đề, không phải dữ liệu. Hàng đầu tiên của bảng có lẽ nên được định dạng bằng <th>...</th>thẻ thay vì <td>...</td>thẻ.

Xử lý từng dòng

Bạn đang đọc toàn bộ tệp CSV vào bộ nhớ và chỉ khi nó đã được tải toàn bộ, bạn mới ghi nó ra giấy dưới dạng HTML. Điều này rất tốn bộ nhớ, đặc biệt nếu tệp CSV lớn!

Thay vào đó, bạn có thể:

  • mở CSV
  • mở tệp HTML
  • viết phần mở đầu HTML
  • cho mỗi dòng được đọc từ tệp CSV:
    • định dạng và ghi dòng vào tệp HTML
  • viết HTML epilog

Chưa được kiểm tra, mã hóa từ đầu, không xử lý trích dẫn trong CSV hoặc thoát bất kỳ thực thể HTML nào trong đầu ra:

        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

Bạn có thể muốn xem xét việc tạo một trình dịch CSV sang XML.

Đầu ra XML của bạn có thể trông giống như sau:

<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>

Và sau đó, bạn có thể sử dụng Biểu định kiểu XSLT để dịch XML sang HTML, có thể trong trình duyệt mà không cần ghi HTML vào tệp.

3 Noname Sep 10 2020 at 23:23

Có thể NullPointerExceptionở hàngif (args[0].equals...

Vui lòng kiểm tra argskích thước đầu vào và nullifyingtrước khi tiếp tục truy cập bất kỳ chỉ mục / mục nào.
Có, điều đó argscó thể được null, như được gọi bởi một lớp được tải khác. Hoặc trống, nếu người dùng quên đặt args.

Tập tin đệm

Tôi nghĩ rằng lưu toàn bộ tệp vào bộ nhớ và sau đó xử lý nó là một ý tưởng hay cho trường hợp của bạn, vì mỗi dòng bạn đọc, bạn có thể chỉ cần xử lý và ghi nó, và tiếp tục cho dòng tiếp theo. (như Xử lý từng dòng được AJNeufeld đề cập)

Tách cột bị hỏng (dấu phân cách)

Về cơ bản, việc tách dữ liệu cột bằng cách sử dụng lines.get(i).replaceAll(",",...)bị hỏng, vì bản thân dữ liệu sẽ có ,nội dung.

Xem xét một dòng là Porsche,"991,991.2,992",70nơi mã của bạn (và thậm chí cả đoạn được cung cấp bởi AJNeufeld) sẽ không thành công, vì 991,991.2,992là một giá trị và những dòng ""đó ở đó để cho trình phân tích cú pháp biết, dữ liệu thoát đang ở phía trước.

Vì vậy, về mặt cá nhân, tôi khuyên bạn nên thực hiện quy trình phân tích cú pháp char-by-char, cho phép bạn bỏ qua bất kỳ ,dấu phân cách nào khi bạn đến phần mở đầu "cho đến "cặp kết thúc của nó .

Các chuỗi không mong đợi

Cũng đang xem xét xác nhận, bỏ qua hoặc chuyển đổi bất kỳ ký tự không mong muốn nào. Ví dụ: chuyển đổi một ký tự NULL( \0) thành 0x00hoặc hiển thị cảnh báo và bỏ qua nó.

Quên tệp

Tôi khuyên bạn không nên khóa ứng dụng của mình để chỉ đọc từ các tệp và lưu vào chúng. Nhiều lần STDIN, và STDOUTlà những cách được hoan nghênh hơn.

Tôi đề nghị hỗ trợ từ cả tệp và STDIN. Ví dụ: sử dụng phải đặt -fincho đầu vào tệp trong đối số hoặc -stdinđể thông báo cho công cụ đọc từ đó STDIN.

Kiểm tra tệp

Kiểm tra tệp (cả vào / ra) trước khi xử lý tệp cũng sẽ rất tuyệt. Để đảm bảo rằng chúng có thể truy cập được.

Quả quyết

Dọn dẹp (hoặc nhắc nhở / có thể thiết lập) kết quả, trong khi có một ngoại lệ IO không mong muốn trong quá trình cũng có thể tốt.

Hy vọng nó giúp.