簡體   English   中英

字符編碼將 windows-1252 輸入文件轉換為 utf-8 輸出文件

[英]Character encoding converting windows-1252 input file to utf-8 output file

我正在處理我從 Word 的保存選項(以編程方式)轉換為 HTML 的 HTML 文檔。 這個 HTML 文本文件是 windows-1252 編碼的。 (是的,我已經閱讀了很多關於字節和 Unicode 代碼點的內容,我知道超過 128 的代碼點可以是 2,3,最多 6 個字節等。)我在 Word 文檔模板中添加了很多不可打印的字符並編寫代碼來評估每個 CHARACTER(十進制等效值)。 當然,我知道我不想允許十進制 #160,這是 MS Word 翻譯成 HTML 的不間斷空格。 我預計在不久的將來人們會將更多這些“非法”構造放入模板中,我將需要捕獲它們並處理它們(因為它們會在瀏覽器中引起有趣的查看:(這是在轉儲到 Eclipse 控制台,我將所有文檔行放入地圖中)

 DataObj.paragraphMap  : {1=, 2=Introduction and Learning Objective, 3=? ©®™§¶…‘’“”????, 4=, 5=, 6=, 
   7=This is paragraph 1 no formula, 8=, 

我用 #32(常規空格)替換了十進制 #160,然后使用 UTF-8 編碼將字符寫入新文件 - 我的想法也是如此,我可以使用這種技術來替換或決定不寫回特定字符嗎使用十進制等價? 我想避免使用字符串,因為我可以處理多個文檔並且不想耗盡內存......所以我在文件中進行......

 public static void convert1252toUFT8(String fileName) throws IOException {   
    File f = new File(fileName);
    Reader r = new BufferedReader(new InputStreamReader(new FileInputStream(f), "windows-1252"));
    OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(fileName + "x"), StandardCharsets.UTF_8); 
    List<Character> charsList = new ArrayList<>(); 
    int count = 0;

    try {
        int intch;
        while ((intch = r.read()) != -1) {   //reads a single character and returns integer equivalent
            int ch = (char)intch;
            //System.out.println("intch=" + intch + " ch=" + ch + " isValidCodePoint()=" + Character.isValidCodePoint(ch) 
            //+ " isDefined()=" + Character.isDefined(ch) + " charCount()=" + Character.charCount(ch) + " char=" 
            //+ (char)intch);

            if (Character.isValidCodePoint(ch)) {
                if (intch == 160 ) {
                    intch = 32;
                }
                charsList.add((char)intch);
                count++;
            } else {
                System.out.println("unexpected character found but not dealt with.");
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        System.out.println("Chars read in=" + count + " Chars read out=" + charsList.size());
        for(Character item : charsList) {
            writer.write((char)item);
        }
        writer.close();
        r.close();
        charsList = null;

        //check that #160 was replaced File 
        //f2 = new File(fileName + "x"); 
        //Reader r2 = new BufferedReader(new InputStreamReader(new FileInputStream(f2), "UTF-8")); 
        //int intch2;
        //while ((intch2 = r2.read()) != -1) { //reads a single character and returns integer equivalent 
        //int ch2 = (char)intch2; 
        //System.out.println("intch2=" + intch2 + " ch2=" + ch2 + " isValidCodePoint()=" +
        //Character.isValidCodePoint(ch2) + " char=" + (char)intch2); 
        //}

    }   
}

首先,HTML 頁面采用與 UTF-8 不同的編碼並沒有錯。 事實上,文檔很可能包含這樣一行

<meta http-equiv="Content-Type" content="text/html; charset=windows-1252">

在其標題中,當您更改文件的字符編碼而不調整此標題行時,這會使文檔無效。

此外,沒有理由替換文檔中的代碼點 #160,因為它是 Unicode 的標准非中斷空格字符,這就是為什么&#160; &nbsp;的有效替代品如果文檔的字符集支持這個代碼點,直接使用它也是有效的。

您嘗試避免使用字符串是過早優化的典型案例。 缺乏實際測量導致了像ArrayList<Character>這樣的解決方案,它消耗了String兩倍的內存。

如果要復制或轉換文件,則不應將整個文件保存在內存中。 只需在讀取下一個之前將數據寫回,但為了效率起見,使用一些緩沖區而不是一次讀取和寫入單個字符。 此外,您應該使用try-with-resources 語句來管理輸入和輸出資源。

public static void convert1252toUFT8(String fileName) throws IOException {
    Path in = Paths.get(fileName), out = Paths.get(fileName+"x");
    int readCount = 0, writeCount = 0;
    try(BufferedReader br = Files.newBufferedReader(in, Charset.forName("windows-1252"));
        BufferedWriter bw = Files.newBufferedWriter(out, // default UTF-8
            StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {

        char[] buffer = new char[1000];
        do {
            int count = br.read(buffer);
            if(count < 0) break;
            readCount += count;

            // if you really want to replace non breaking spaces:
            for(int ix = 0; ix < count; ix++) {
                if(buffer[ix] == 160) buffer[ix] = ' ';
            }

            bw.write(buffer, 0, count);
            writeCount += count;
        } while(true);
    } finally {
        System.out.println("Chars read in="+readCount+" Chars written out="+writeCount);
    }
}

測試字符的有效性沒有意義,因為解碼器不會產生無效的代碼點。 默認情況下,解碼器配置為對無效字節拋出異常。 其他選項是用替換字符(如 )替換無效輸入或跳過它們,但它永遠不會產生無效字符。

操作期間所需的內存量由緩沖區大小決定,盡管上面的代碼使用了各自具有緩沖區的讀取器和寫入器。 用於操作的內存總量仍然與文件大小無關。

僅使用您明確指定的緩沖區的解決方案看起來像

public static void convert1252toUFT8(String fileName) throws IOException {
    Path in = Paths.get(fileName), out = Paths.get(fileName+"x");
    int readCount = 0, writeCount = 0;
    try(Reader br = Channels.newReader(Files.newByteChannel(in), "windows-1252");
        Writer bw = Channels.newWriter(
            Files.newByteChannel(out, WRITE, CREATE, TRUNCATE_EXISTING),
            StandardCharsets.UTF_8)) {

        char[] buffer = new char[1000];
        do {
            int count = br.read(buffer);
            if(count < 0) break;
            readCount += count;

            // if you really want to replace non breaking spaces:
            for(int ix = 0; ix < count; ix++) {
                if(buffer[ix] == 160) buffer[ix] = ' ';
            }

            bw.write(buffer, 0, count);
            writeCount += count;
        } while(true);
    } finally {
        System.out.println("Chars read in="+readCount+" Chars written out="+writeCount);
    }
}

這也將是實現對無效輸入的不同處理的起點,例如,只需刪除所有無效輸入字節,您只需將方法的開頭更改為

public static void convert1252toUFT8(String fileName) throws IOException {
    Path in = Paths.get(fileName), out = Paths.get(fileName+"x");
    int readCount = 0, writeCount = 0;
    CharsetDecoder dec = Charset.forName("windows-1252")
            .newDecoder().onUnmappableCharacter(CodingErrorAction.IGNORE);
    try(Reader br = Channels.newReader(Files.newByteChannel(in), dec, -1);
…

注意,對於一次成功的轉換,讀取和寫入的字符數是相同的,但僅對於輸入編碼Windows-1252,字符數與字節數相同,即文件大小(當整個文件有效時)。

這個轉換代碼示例只是為了完成,正如開頭所說,在不調整標題的情況下轉換 HTML 頁面可能會使文件無效,甚至沒有必要。

¹取決於實施,甚至四次

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM