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