1. 概述

非法字符编译错误本质是文件编码类型问题。当创建文件时使用了错误的编码方式,就会在Java等语言编译时触发此错误。本文将深入剖析该问题,分析常见踩坑场景,并提供解决方案。

2. 非法字符编译错误

2.1. 字节顺序标记(BOM)

理解BOM前需先了解UCS(Unicode)转换格式(UTF)。UTF是一种能编码所有Unicode字符的编码格式,其中UTF-8使用最广泛。

UTF-8采用8位变长编码,最大限度兼容ASCII。使用此编码时,文件可能以U+FEFF字节顺序标记(BOM)开头。正常情况下BOM不可见,但可能引发数据错误。

在UTF-8中,BOM并非必需。某些文本编辑器(如Windows记事本)会自动添加BOM,导致:

  • 编译时出现非法字符错误
  • 文件读取时产生不可见字符

现代IDE通常采用无BOM的UTF-8编码,可避免此问题。后续将通过示例说明。

2.2. 非法字符编译错误示例

虽然我们常用高级IDE,但有时会使用文本编辑器。某些编辑器保存文件时添加BOM,会导致Java编译报错。该错误在编译阶段即可发现,示例如下:

在记事本中创建测试类:

public class TestBOM {
    public static void main(String ...args){
        System.out.println("BOM Test");
    }
}

使用javac编译:

$ javac ./TestBOM.java

得到报错信息:

public class TestBOM {
 ^
.\TestBOM.java:1: error: illegal character: '\u00bf'
public class TestBOM {
  ^
2 errors

解决方案

  1. 将文件另存为无BOM的UTF-8编码
  2. 使用dos2unix工具移除BOM
  3. 始终检查文件编码是否包含BOM

3. 读取含BOM文件

下面分析读取含BOM文件的常见场景。首先创建测试文件,内容为"Hello world with BOM."。

3.1. 使用BufferedReader读取

@Test
public void whenInputFileHasBOM_thenUseInputStream() throws IOException {
    String line;
    String actual = "";
    try (BufferedReader br = new BufferedReader(new InputStreamReader(file))) {
        while ((line = br.readLine()) != null) {
            actual += line;
        }
    }
    assertEquals(expected, actual);
}

测试结果

org.opentest4j.AssertionFailedError: expected: <Hello world with BOM.> but was: <Hello world with BOM.>
Expected :Hello world with BOM.
Actual   :Hello world with BOM.

虽然字符串看起来相同,但实际值包含BOM字符。简单粗暴的解决方案

@Test
public void whenInputFileHasBOM_thenUseInputStreamWithReplace() throws IOException {
    String line;
    String actual = "";
    try (BufferedReader br = new BufferedReader(new InputStreamReader(file))) {
        while ((line = br.readLine()) != null) {
            actual += line.replace("\uFEFF", "");
        }
    }
    assertEquals(expected, actual);
}

⚠️ 注意:处理大量文件时,replace方法可能影响性能。

3.2. 使用Apache Commons IO

Apache Commons IO库提供了BOMInputStream类,专门处理含BOM的文件:

@Test
public void whenInputFileHasBOM_thenUseBOMInputStream() throws IOException {
    String line;
    String actual = "";
    ByteOrderMark[] byteOrderMarks = new ByteOrderMark[] { 
      ByteOrderMark.UTF_8, ByteOrderMark.UTF_16BE, ByteOrderMark.UTF_16LE, ByteOrderMark.UTF_32BE, ByteOrderMark.UTF_32LE
    };
    InputStream inputStream = new BOMInputStream(ioStream, false, byteOrderMarks);
    Reader reader = new InputStreamReader(inputStream);
    BufferedReader br = new BufferedReader(reader);
    while ((line = br.readLine()) != null) {
        actual += line;
    }
    assertEquals(expected, actual);
}

3.3. 使用Google Data (GData)

Google Data (GData)库是另一个处理BOM的有效工具(虽较老但实用):

@Test
public void whenInputFileHasBOM_thenUseGoogleGdata() throws IOException {
    char[] actual = new char[21];
    try (Reader r = new UnicodeReader(ioStream, null)) {
        r.read(actual);
    }
    assertEquals(expected, String.valueOf(actual));
}

4. 总结

本文系统分析了Java中的非法字符编译错误:

  1. 核心问题:文件编码中的BOM导致编译/读取异常
  2. 常见场景
    • 使用记事本等编辑器创建Java文件
    • 读取含BOM的文本文件
  3. 解决方案
    • 保存文件时选择无BOM的UTF-8编码
    • 使用专业工具(如dos2unix)
    • 采用Apache Commons IO或GData库处理文件读取

关键建议:始终检查文件编码,避免在代码中硬编码处理BOM。所有示例代码可在GitHub获取。


原始标题:Illegal Character Compilation Error | Baeldung