【问题标题】:Reliance on default encoding, what should I use and why?依赖默认编码,我应该使用什么以及为什么?
【发布时间】:2014-04-02 15:22:42
【问题描述】:

FindBugs 报告了一个错误:

依赖默认编码 找到了对将执行字节到字符串(或字符串到字节)转换的方法的调用,并假定默认平台编码是合适的。这将导致应用程序行为因平台而异。使用替代 API 并明确指定字符集名称或字符集对象。

我是这样使用 FileReader 的(只是一段代码):

public ArrayList<String> getValuesFromFile(File file){
    String line;
    StringTokenizer token;
    ArrayList<String> list = null;
    BufferedReader br = null;
    try {
        br = new BufferedReader(new FileReader(file));
        list = new ArrayList<String>();
        while ((line = br.readLine())!=null){
            token = new StringTokenizer(line);
            token.nextToken();
            list.add(token.nextToken());
    ...

为了纠正这个错误我需要改变

br = new BufferedReader(new FileReader(file));

br = new BufferedReader(new InputStreamReader(new FileInputStream(file), Charset.defaultCharset()));

当我使用 PrintWriter 时,发生了同样的错误。所以现在我有一个问题。当我可以(应该)使用 FileReader 和 PrintWriter 时,如果不是好习惯,请依赖默认编码? 而第二个问题是正确使用Charset.defaultCharset()?我决定使用这种方法来自动定义用户操作系统的字符集。

【问题讨论】:

    标签: java encoding internationalization findbugs


    【解决方案1】:

    理想情况下应该是:

    try (InputStream in = new FileInputStream(file);
         Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);
         BufferedReader br = new BufferedReader(reader)) {
    

    ...或:

    try (BufferedReader br = Files.newBufferedReader(path, StandardCharsets.UTF_8)) {
    

    ...假设文件被编码为 UTF-8。

    对于自然语言数据,几乎所有不是 Unicode 转换格式的编码都已过时。有些语言没有 Unicode 就无法支持。

    【讨论】:

      【解决方案2】:

      如果文件在您的应用程序的控制之下,并且如果您希望文件以平台的默认编码进行编码,那么您可以使用默认平台编码。明确指定它会使您和未来的维护者更清楚这是您的意图。例如,对于文本编辑器来说,这将是一个合理的默认设置,然后它将写入该平台上的任何其他编辑器都可以读取的文件。

      另一方面,如果您想确保可以在文件中写入任何可能的字符,则应使用通用编码,如 UTF8。

      如果文件来自外部应用程序,或者应该与外部应用程序兼容,那么您应该使用该外部应用程序期望的编码。

      您必须意识到,如果您像在一台机器上那样编写文件,并像在另一台没有相同默认编码的机器上那样读取它,则不一定能够阅读您所写的内容。使用特定的编码进行写入和读取,例如 UTF8,确保文件始终相同,无论写入文件时使用什么平台。

      【讨论】:

      • 值得建议的是,即使使用默认编码,为了清楚起见,它也被显式指定。
      • 你刚刚做到了 :-) 我在第一段中添加了一个句子。谢谢。
      • 好的,但是如果我的应用程序应该与外部应用程序兼容,我该怎么办,但我不知道它是编码。 Charset.defaultCharset() 方法是否允许确定这种编码?
      • 阅读外部应用的文档。使用它的 GUI 并尝试发现它使用的编码。或者用它来写各种字符(ascii、occidental、chinese等),自己用各种编码做同样的事情,对比生成的文件看用的是哪种编码。好的文本编辑器有试探法来尝试猜测文件中使用的编码。因此,您也可以尝试使用这样的编辑器打开由外部应用程序生成的文件,看看它猜到了什么。
      【解决方案3】:

      每当您读取应用程序外部的文件并且可以假定为用户本地编码的文件时,您都应该使用默认编码,例如用户编写的文本文件。您可能希望在编写此类文件时使用默认编码,具体取决于用户稍后将对该文件执行的操作。

      您不应该对任何其他文件使用默认编码,尤其是与应用程序相关的文件。

      例如,如果您的应用程序以文本格式编写配置文件,则应始终指定编码。一般来说,UTF-8 总是一个不错的选择,因为它几乎兼容所有东西。不这样做可能会导致其他国家/地区的用户意外崩溃。

      这不仅限于字符编码,还包括日期/时间、数字或其他语言特定格式。例如,如果您在美国机器上使用默认编码和默认日期/时间字符串,然后尝试在德国服务器上读取该文件,您可能会惊讶为什么一半是乱码而另一半的月份/天数混乱或关闭由于夏令时,一小时。

      【讨论】:

        【解决方案4】:

        当您使用 PrintWriter 时,

        File file = new File(file_path);
        Writer w = new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_16.name());
        PrintWriter pw = new PrintWriter(w);
        pw.println(content_to_write);
        pw.close();
        

        【讨论】:

          【解决方案5】:

          这将起作用:-

          FileReader file = new FileReader(csvFile, Charset.forName("UTF-8"));
          
          BufferedReader csvReader = new BufferedReader(file);
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-05-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-12-07
            • 1970-01-01
            • 2011-06-07
            • 2013-03-10
            相关资源
            最近更新 更多