Java读取文件时带有BOM头导致出现特殊字符 []

7
我正在逐行阅读一个包含关键词的文件,发现了一个奇怪的问题。 如果它们的内容相同,我希望连续的行只被处理一次。比如:
sony
sony

只有第一个被处理。 但问题是,Java不将它们视为相等。

INFO: [, s, o, n, y]
INFO: [s, o, n, y]

我的代码如下,有什么问题吗?

    FileReader fileReader = new FileReader("some_file.txt");
    BufferedReader bufferedReader = new BufferedReader(fileReader);
    String prevLine = "";
    String strLine
    while ((strLine = bufferedReader.readLine()) != null) {
        logger.info(Arrays.toString(strLine.toCharArray()));
        if(strLine.contentEquals(prevLine)){
            logger.info("Skipping the duplicate lines " + strLine);
            continue;
        }
        prevLine = strLine;
    }

更新:

第一行似乎有一个前导空格,但实际上并没有,而trim方法对我无效。它们不是相同的:

INFO: [, s, o, n, y]
INFO: [ , s, o, n, y]

我不知道Java添加的第一个字符是什么。
解决:问题已使用 BalusC的解决方案解决,感谢您指出了这是BOM问题,这帮助我快速找到了解决方案。

1
文件是否以字节序列“ef bb bf”开始?如果是,则它是一个带有BOM的UTF-8文件。 - McDowell
不是,它是UTF编码,但不以你提到的序列开头。 - Sawyer
请发布前两行的十六进制转储和系统的默认字符集。否则,我们只是在玩“猜测代码点”的游戏。 - McDowell
7个回答

2
文件的编码是什么?
文件开头看不到的字符可能是 字节顺序标记
使用ANSI或UTF-8但没有BOM保存可帮助您更好地发现这一点。

2
Byte Order Mark(BOM)是Unicode字符。在文本流的开头,您会得到像这样的字符,因为BOM的使用是可选的,如果使用,则应出现在文本流的开头。
Microsoft编译器和解释器以及许多在Microsoft Windows上运行的软件(如记事本)将BOM视为必需的幻数,而不是使用启发式方法。这些工具在将文本保存为UTF-8时添加BOM,并且除非BOM存在或文件仅包含ASCII,否则无法解释UTF-8。Google Docs在将文档转换为纯文本文件进行下载时也会添加BOM。
File file = new File( csvFilename );
FileInputStream inputStream = new FileInputStream(file);
// [{"Key2":"21","Key1":"11","Key3":"31"} ]
InputStreamReader inputStreamReader = new InputStreamReader( inputStream, "UTF-8" );

我们可以通过显式指定字符集为UTF-8到InputStreamReader来解决问题。然后在UTF-8中,字节序列解码为一个字符,即U+FEFF(?)。
使用Google Guava's jar CharMatcher,您可以删除任何不可打印字符,然后保留所有ASCII字符(删除任何重音符号),如this所示:
String printable = CharMatcher.INVISIBLE.removeFrom( input );
String clean = CharMatcher.ASCII.retainFrom( printable );

完整示例:从CSV文件读取数据到JSON对象

public class CSV_FileOperations {
    static List<HashMap<String, String>> listObjects = new ArrayList<HashMap<String,String>>();
    protected static List<JSONObject> jsonArray = new ArrayList<JSONObject >();

    public static void main(String[] args) {
        String csvFilename = "D:/Yashwanth/json2Bson.csv";

        csvToJSONString(csvFilename);
        String jsonData = jsonArray.toString();
        System.out.println("File JSON Data : \n"+ jsonData);
    }

    @SuppressWarnings("deprecation")
    public static String csvToJSONString( String csvFilename ) {
        try {
            File file = new File( csvFilename );
            FileInputStream inputStream = new FileInputStream(file);

            String fileExtensionName = csvFilename.substring(csvFilename.indexOf(".")); // fileName.split(".")[1];
            System.out.println("File Extension : "+ fileExtensionName);

            // [{"Key2":"21","Key1":"11","Key3":"31"} ]
            InputStreamReader inputStreamReader = new InputStreamReader( inputStream, "UTF-8" );

            BufferedReader buffer = new BufferedReader( inputStreamReader );
            Stream<String> readLines = buffer.lines();
            boolean headerStream = true;

            List<String> headers = new ArrayList<String>();
            for (String line : (Iterable<String>) () -> readLines.iterator()) {
                String[] columns = line.split(",");
                if (headerStream) {
                    System.out.println(" ===== Headers =====");

                    for (String keys : columns) {
                        //  - UTF-8 - ? « https://dev59.com/w2gu5IYBdhLWcg3w3ara#11021401
                        String printable = CharMatcher.INVISIBLE.removeFrom( keys );
                        String clean = CharMatcher.ASCII.retainFrom(printable);
                        String key = clean.replace("\\P{Print}", "");
                        headers.add( key );
                    }
                    headerStream = false;
                    System.out.println(" ===== ----- Data ----- =====");
                } else {
                    addCSVData(headers, columns );
                }
            }
            inputStreamReader.close();
            buffer.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    @SuppressWarnings("unchecked")
    public static void addCSVData( List<String> headers, String[] row ) {
        if( headers.size() == row.length ) {
            HashMap<String,String> mapObj = new HashMap<String,String>();
            JSONObject jsonObj = new JSONObject();
            for (int i = 0; i < row.length; i++) {
                mapObj.put(headers.get(i), row[i]);
                jsonObj.put(headers.get(i), row[i]);
            }
            jsonArray.add(jsonObj);
            listObjects.add(mapObj);
        } else {
            System.out.println("Avoiding the Row Data...");
        }
    }
}

"

json2Bson.csv 文件数据。

"
Key1    Key2    Key3
11  21  31
12  22  32
13  23  33

1

我在之前的项目中遇到过类似的情况。罪魁祸首是 字节顺序标记,我必须摆脱它。最终,我根据这个例子实现了一个hack。看看吧,也许你有同样的问题。


1

尝试去除读取行开头和结尾的空格。只需将您的 while 替换为:

while ((strLine = bufferedReader.readLine()) != null) {
        strLine = strLine.trim();
        logger.info(Arrays.toString(strLine.toCharArray()));
    if(strLine.contentEquals(prevLine)){
        logger.info("Skipping the duplicate lines " + strLine);
        continue;
    }
    prevLine = strLine;
}

@Sawyer - 你的文本文件可能存在编码问题。我刚刚尝试了你的示例,它可以完美地工作。 - Nico Huysamen

0
如果空格在处理中不重要,每次进行strLine.trim()调用可能是值得的。这是我处理此类输入时通常采用的方法 - 如果必须手动编辑文件,则空格很容易出现,如果它们不重要,则可以忽略它们。
编辑:文件是否以UTF-8编码?打开文件时可能需要指定编码。如果发生在第一行上,可能是字节顺序标记或类似的东西。
尝试:
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF8"))

1
@Sawyer 当你打开文件时,可能需要指定文件编码,我已编辑过了。 - user7094
是的,它是用UTF-8编码的,我尝试了你的代码,但不起作用。 - Sawyer

0

开头必须有一个空格或一些不可打印的字符。因此,在比较之前/期间修复它或修剪Strings

[编辑]

如果String.trim()无效,请尝试使用适当的regex进行String.replaceAll()。尝试这个:str.replaceAll("\\p{Cntrl}", "")


0

在文本编辑器中打开文件,导航到文件 > 另存为...并选择UTF-8编码,而不是带BOM的UTF-8


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接