ChatGPT解决这个技术问题 Extra ChatGPT

如何通过 ResourceBundle 在资源属性中使用 UTF-8

我需要使用 Java 的 ResourceBundle 在我的资源属性中使用 UTF-8。当我将文本直接输入属性文件时,它显示为 mojibake。

我的应用在 Google App Engine 上运行。

谁能给我一个例子?我不能得到这个工作。

Java 1.6 修复了这个问题,因为您可以传入 Reader。请参阅下面的@Chinaxing 答案
@Will:问题主要是关于通过 java.util.ResourceBundle 而不是 java.util.Properties 阅读它们。
检查这个已回答的问题,希望对您有所帮助 [stackoverflow.com/questions/863838/… [1]: stackoverflow.com/questions/863838/…
JDK9 应原生支持 UTF-8,请参阅 JEP 226

B
BalusC

Java 9 和更新版本

From Java 9 onwards 属性文件默认编码为 UTF-8,使用 ISO-8859-1 以外的字符应该可以直接使用。

Java 8 及更早版本

ResourceBundle#getBundle() 在指定 .properties 文件时使用 PropertyResourceBundle。这反过来又默认使用 Properties#load(InputStream) 来加载这些属性文件。根据 the javadoc,它们默认读取为 ISO-8859-1。

公共无效负载(InputStream inStream)抛出 IOException

从输入字节流中读取属性列表(键和元素对)。输入流采用 load(Reader) 中指定的简单的面向行的格式,并假定使用 ISO 8859-1 字符编码;也就是说,每个字节都是一个 Latin1 字符。非拉丁语1 中的字符和某些特殊字符使用Java™ 语言规范第3.3 节中定义的Unicode 转义表示在键和元素中。

因此,您需要将它们保存为 ISO-8859-1。如果您有任何超出 ISO-8859-1 范围的字符,并且您不能在头顶使用 \uXXXX,因此您被迫将文件另存为 UTF-8,那么您需要使用 {1 } 工具将 UTF-8 保存的属性文件转换为 ISO-8859-1 保存的属性文件,其中所有未覆盖的字符都转换为 \uXXXX 格式。下面的示例将 UTF-8 编码的属性文件 text_utf8.properties 转换为有效的 ISO-8859-1 编码的属性文件 text.properties

native2ascii -encoding UTF-8 text_utf8.properties text.properties

在使用 Eclipse 等健全的 IDE 时,当您在基于 Java 的项目中创建 .properties 文件并使用 Eclipse 自己的编辑器时,这已经自动完成。 Eclipse 会将超出 ISO-8859-1 范围的字符透明地转换为 \uXXXX 格式。另请参阅下面的屏幕截图(注意底部的“属性”和“源”选项卡,点击查看大图):

https://i.stack.imgur.com/iQWWGm.png

或者,您也可以创建一个自定义 ResourceBundle.Control 实现,其中您使用 InputStreamReader 将属性文件显式读取为 UTF-8,这样您就可以将它们保存为 UTF-8,而无需使用 native2ascii。这是一个启动示例:

public class UTF8Control extends Control {
    public ResourceBundle newBundle
        (String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
            throws IllegalAccessException, InstantiationException, IOException
    {
        // The below is a copy of the default implementation.
        String bundleName = toBundleName(baseName, locale);
        String resourceName = toResourceName(bundleName, "properties");
        ResourceBundle bundle = null;
        InputStream stream = null;
        if (reload) {
            URL url = loader.getResource(resourceName);
            if (url != null) {
                URLConnection connection = url.openConnection();
                if (connection != null) {
                    connection.setUseCaches(false);
                    stream = connection.getInputStream();
                }
            }
        } else {
            stream = loader.getResourceAsStream(resourceName);
        }
        if (stream != null) {
            try {
                // Only this line is changed to make it to read properties files as UTF-8.
                bundle = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"));
            } finally {
                stream.close();
            }
        }
        return bundle;
    }
}

这可以按如下方式使用:

ResourceBundle bundle = ResourceBundle.getBundle("com.example.i18n.text", new UTF8Control());

也可以看看:

Unicode - 如何让字符正确?


谢谢。顺便说一句,重写 getFormats 以返回 FORMAT_PROPERTIES 似乎是个好主意。
您能否详细说明这个覆盖 getFormats() 的建议?
如果您使用的是 Java 7+,请不要犹豫使用 StandardCharsets.UTF_8
@Nyerguds:如果您看到以编程方式更改它的理由(尽管我无法想象),请随意这样做。毕竟,我发布的所有代码片段都只是启动示例。
我认为 newBundle 方法应该以 if(!format.equals("java.properties")) return super.newBundle(…); 开头,以保持其他包格式(如定位和加载 ResourceBundle 的子类)不变。
R
Regexident

假设您有一个 ResourceBundle 实例,您可以通过以下方式获取 String:

String val = bundle.getString(key); 

我通过以下方式解决了我的日语显示问题:

return new String(val.getBytes("ISO-8859-1"), "UTF-8");

对于这里所有天真的支持者/评论者:这不是解决方案,而是解决方法。真正的根本问题仍然存在并且需要解决。
这解决了我的情况。解决方案是让 Java 开始在资源包和属性文件中本地处理 UTF-8。在这种情况发生之前,我将使用一种解决方法。
@BalusC;这种方法的缺点是什么? (除了创建一个额外的字符串?)
@Paaske:这是一种解决方法,而不是解决方案。您需要在整个代码库中的所有字符串变量的所有位置重新应用解决方法。这纯粹是胡说八道。只需将其修复在一个位置,在正确的位置,以便字符串变量立即包含正确的值。绝对不需要修改客户端。
是的,如果您必须修改整个应用程序,这当然很糟糕。但是,如果您已经将 ResourceBundle 作为单例使用,则只需修复一次。我的印象是单例方法是使用 ResourceBundle 的最常见方式。
d
dedek

看看这个:http://docs.oracle.com/javase/6/docs/api/java/util/Properties.html#load(java.io.Reader)

这些属性接受一个 Reader 对象作为参数,您可以从 InputStream 创建它。

在创建时,可以指定Reader的编码:

InputStreamReader isr = new InputStreamReader(stream, "UTF-8");

然后将此 Reader 应用于加载方法:

prop.load(isr);

顺便说一句:从 .properties 文件中获取流:

 InputStream stream = this.class.getClassLoader().getResourceAsStream("a.properties");

顺便说一句:从 InputStreamReader 获取 资源包

ResourceBundle rb = new PropertyResourceBundle(isr);

希望这可以帮到你 !


不过,这里的实际问题是关于 ResourceBundle
诚然,如果您使用 Properties 并且您想检索 UTF-8 字符串,这应该被接受,那么这就像一个魅力。但是对于诸如语言资源之类的 ResourceBundle,那么接受的答案是优雅的。尽管如此,还是对答案投了赞成票。
ResourceBundle rb = new PropertyResourceBundle(new InputStreamReader(stream, "UTF-8"))
但是如何获取指定语言的流? a.properties 是文件名,而捆绑包名称是 a
s
stenix

这个问题终于在 Java 9 中得到修复:https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9

属性文件的默认编码现在是 UTF-8。

大多数现有属性文件不应受到影响:UTF-8 和 ISO-8859-1 对 ASCII 字符具有相同的编码,而人类可读的非 ASCII ISO-8859-1 编码不是有效的 UTF-8。如果检测到无效的 UTF-8 字节序列,Java 运行时会自动重新读取 ISO-8859-1 中的文件。


K
Kariem

例如,如果属性文件使用 cp1251 字符集,则使用 UTF-8 和新字符串方法的 ResourceBundle.Control 不起作用。

所以我推荐使用一个通用的方法:用 unicode 符号写。为了这:

IDEA -- 有一个特殊的Transparent native-to-ASCII conversion 选项(设置 > 文件编码)。

Eclipse -- 有一个插件Properties Editor。它可以作为单独的应用程序工作。


在 IntelliJ IDEA 14 中,它位于设置 -> 编辑器 -> 文件编码中。我还必须删除任何现有的属性文件,并重新创建它们以使此选项生效。
IDE 与答案并不特别相关,而只是工具,它们确实不能解决不将内容存储在 UTF-8 字符集中的根本问题......这将立即解决问题,而无需转换或像编写属性这样的黑客行为在使用不同字符集定义的文件内的 unicode 符号中。
m
marcolopes
package com.varaneckas.utils;  

import java.io.UnsupportedEncodingException;  
import java.util.Enumeration;  
import java.util.PropertyResourceBundle;  
import java.util.ResourceBundle;  

/** 
 * UTF-8 friendly ResourceBundle support 
 *  
 * Utility that allows having multi-byte characters inside java .property files. 
 * It removes the need for Sun's native2ascii application, you can simply have 
 * UTF-8 encoded editable .property files. 
 *  
 * Use:  
 * ResourceBundle bundle = Utf8ResourceBundle.getBundle("bundle_name"); 
 *  
 * @author Tomas Varaneckas <tomas.varaneckas@gmail.com> 
 */  
public abstract class Utf8ResourceBundle {  

    /** 
     * Gets the unicode friendly resource bundle 
     *  
     * @param baseName 
     * @see ResourceBundle#getBundle(String) 
     * @return Unicode friendly resource bundle 
     */  
    public static final ResourceBundle getBundle(final String baseName) {  
        return createUtf8PropertyResourceBundle(  
                ResourceBundle.getBundle(baseName));  
    }  

    /** 
     * Creates unicode friendly {@link PropertyResourceBundle} if possible. 
     *  
     * @param bundle  
     * @return Unicode friendly property resource bundle 
     */  
    private static ResourceBundle createUtf8PropertyResourceBundle(  
            final ResourceBundle bundle) {  
        if (!(bundle instanceof PropertyResourceBundle)) {  
            return bundle;  
        }  
        return new Utf8PropertyResourceBundle((PropertyResourceBundle) bundle);  
    }  

    /** 
     * Resource Bundle that does the hard work 
     */  
    private static class Utf8PropertyResourceBundle extends ResourceBundle {  

        /** 
         * Bundle with unicode data 
         */  
        private final PropertyResourceBundle bundle;  

        /** 
         * Initializing constructor 
         *  
         * @param bundle 
         */  
        private Utf8PropertyResourceBundle(final PropertyResourceBundle bundle) {  
            this.bundle = bundle;  
        }  

        @Override  
        @SuppressWarnings("unchecked")  
        public Enumeration getKeys() {  
            return bundle.getKeys();  
        }  

        @Override  
        protected Object handleGetObject(final String key) {  
            final String value = bundle.getString(key);  
            if (value == null)  
                return null;  
            try {  
                return new String(value.getBytes("ISO-8859-1"), "UTF-8");  
            } catch (final UnsupportedEncodingException e) {  
                throw new RuntimeException("Encoding not supported", e);  
            }  
        }  
    }  
}  

我喜欢这个解决方案,并像 Gist gist.github.com/enginer/3168dd4a374994718f0e 一样发布它
这很好用。刚刚在 UTF8 中添加了一个中文翻译属性文件,它加载没有任何问题。
a
andykellr

我们创建了一个包含 UTF-8 资源的 resources.utf8 文件,并有一个规则来运行以下内容:

native2ascii -encoding utf8 resources.utf8 resources.properties

我们从哪里得到 native2ascii?我刚刚做了 find / -name native2ascii* 并没有得到任何结果,所以我认为它不仅仅是 JDK 的一部分......
嗯。它不是 IBM JDK 的一部分,但它似乎包含在 jdk1.*.0_*/bin 中的 Oracle JDK 中。
它似乎是 IBM JDK 的一部分,至少在 JDK 6 中是这样。
R
Ralph

注意:在 Java <= 8 中,Java 属性文件应以 ISO 8859-1 编码!

ISO 8859-1 字符编码。不能用这种编码直接表示的字符可以使用 Unicode 转义来编写;转义序列中只允许使用单个 'u' 字符。

@see 属性 Java 文档

如果您仍然真的想这样做:看看:Java properties UTF-8 encoding in Eclipse - 有一些代码示例

由于Java 9:属性文件以UTF-8编码,所以应该没有问题/怀疑

在 Java SE 9 中,属性文件以 UTF-8 编码加载。在以前的版本中,ISO-8859-1 编码用于加载属性资源包。

(https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm#JSINT-GUID-9DCDB41C-A989-4220-8140-DBFB844A0FCA)


Java != Eclipse... 后者是一个 IDE。更多数据!= Java。 Java支持使用大量字符集进行流处理,这些字符集用于国际化(问题毕竟是关于ResourceBundles)......解决使用UTF-8作为最直接的答案。以目标语言不支持的字符集编写属性文件会使问题变得不必要地复杂化。
@Darell Teague:为 ResouceBundle 加载的属性文件必须是 ISO 8859-1 的“提示”是 java 语句:docs.oracle.com/javase/8/docs/api/java/util/… ...我回答的第二部分只是“提示”如何处理有帽子问题。
由于 java9 属性是 UTF-8
@pdem 感谢对这个旧答案的提示-添加了一个部分来澄清我的提示仅适用于 Java <= 8
f
fmucar

http://sourceforge.net/projects/eclipse-rbe/

如前所述,属性文件应以 ISO 8859-1 编码

您可以使用 Eclipse IDE 的上述插件为您进行 Unicode 转换。


G
Gary

这是一个使用 Guava 出色的支持库和 try-with-resources 构造的 Java 7 解决方案。它使用 UTF-8 读取和写入属性文件,以获得最简单的整体体验。

以 UTF-8 格式读取属性文件:

File file =  new File("/path/to/example.properties");

// Create an empty set of properties
Properties properties = new Properties();

if (file.exists()) {

  // Use a UTF-8 reader from Guava
  try (Reader reader = Files.newReader(file, Charsets.UTF_8)) {
    properties.load(reader);
  } catch (IOException e) {
    // Do something
  }
}

要将属性文件编写为 UTF-8:

File file =  new File("/path/to/example.properties");

// Use a UTF-8 writer from Guava
try (Writer writer = Files.newWriter(file, Charsets.UTF_8)) {
  properties.store(writer, "Your title here");
  writer.flush();
} catch (IOException e) {
  // Do something
}

这个答案很有用。这里有各种答案的核心问题似乎是对数据和字符集的误解。 Java 可以通过简单地指定存储数据的字符集来(正确地)读取任何数据,如上所示。 UTF-8 通常用于支持地球上大多数(如果不是所有)语言,因此非常适用于基于 ResourceBundle 的属性。
@DarrellTeague:嗯,“UTF-8 通常用于支持...” - 应该是“Unicode 通常用于支持...”:) 因为 UTF-8 只是一个Unicode (en.wikipedia.org/wiki/UTF-8) 的字符编码。
实际上 UTF-8 是为了被特别称为“字符集”(而不是仅仅引用“任何 Unicode 字符集”),因为 UTF-8 在这种情况下(数据)在 Internet 上的使用占主导地位,某些措施高达67%。参考:stackoverflow.com/questions/8509339/…
M
Masoud

正如有人建议的那样,我完成了资源包的实现..但这并没有帮助..因为该包总是在 en_US 语言环境下调用...我试图将我的默认语言环境设置为另一种语言,但仍然是我的资源包实现正在使用 en_US 调用控制... ...然后我尝试将系统设置默认为 utf8 以通过我的服务器(tomcat 服务器)读取文件..但这导致 pronlem 因为我的所有类库都没有在 utf8 下编译,并且 tomcat 开始以 utf8 格式读取并且服务器没有正常运行......然后我最终在我的java控制器中实现了一个从xhtml文件调用的方法......在那个方法中我做了以下事情:

        public String message(String key, boolean toUTF8) throws Throwable{
            String result = "";
            try{
                FacesContext context = FacesContext.getCurrentInstance();
                String message = context.getApplication().getResourceBundle(context, "messages").getString(key);

                result = message==null ? "" : toUTF8 ? new String(message.getBytes("iso8859-1"), "utf-8") : message;
            }catch(Throwable t){}
            return result;
        }

我特别紧张,因为这可能会降低我的应用程序的性能......但是,在实现这个之后,看起来我的应用程序现在更快了......我认为这是因为,我现在直接访问属性而不是让JSF 解析访问属性的方式...我在此调用中特别传递布尔参数,因为我知道某些属性不会被翻译并且不需要采用 utf8 格式...

现在我已经以 UTF8 格式保存了我的属性文件,并且它工作正常,因为我的应用程序中的每个用户都有一个引用的区域设置首选项。


Z
Zack Bartel

我的问题是文件本身的编码错误。使用 iconv 对我有用

iconv -f ISO-8859-15 -t UTF-8  messages_nl.properties > messages_nl.properties.new

+1 提及 iconv。我以前从未听说过它,但我将它输入控制台并瞧瞧,它是存在的(无论如何,在 CentOS 6 中。)
现在我实际上已经尝试过使用它,但它没有用:它在第一个无法转换为 ISO-8559-1 的字符上抛出。
c
carlossierra

我尝试使用 Rod 提供的方法,但考虑到 BalusC 担心不会在所有应用程序中重复相同的解决方法,并附带了这个类:

import java.io.UnsupportedEncodingException;
import java.util.Locale;
import java.util.ResourceBundle;

public class MyResourceBundle {

    // feature variables
    private ResourceBundle bundle;
    private String fileEncoding;

    public MyResourceBundle(Locale locale, String fileEncoding){
        this.bundle = ResourceBundle.getBundle("com.app.Bundle", locale);
        this.fileEncoding = fileEncoding;
    }

    public MyResourceBundle(Locale locale){
        this(locale, "UTF-8");
    }

    public String getString(String key){
        String value = bundle.getString(key); 
        try {
            return new String(value.getBytes("ISO-8859-1"), fileEncoding);
        } catch (UnsupportedEncodingException e) {
            return value;
        }
    }
}

使用它的方式与常规的 ResourceBundle 用法非常相似:

private MyResourceBundle labels = new MyResourceBundle("es", "UTF-8");
String label = labels.getString(key)

或者您可以使用默认使用 UTF-8 的替代构造函数:

private MyResourceBundle labels = new MyResourceBundle("es");

В
Вассесуарий Пупочкин
Properties prop = new Properties();
String fileName = "./src/test/resources/predefined.properties";
FileInputStream inputStream = new FileInputStream(fileName);
InputStreamReader reader = new InputStreamReader(inputStream,"UTF-8");

N
Neuron

打开设置/首选项对话框 (Ctrl + Alt + S),然后单击编辑器和文件编码。

https://i.stack.imgur.com/NLXei.png

然后,在底部,您将找到属性文件的默认编码。选择您的编码类型。

或者,您可以在资源包中使用 unicode 符号而不是文本(例如 "ів" 等于 \u0456\u0432


F
Fran García

从 Java 9 开始,加载属性文件的默认值已更改为 UTF-8。 https://docs.oracle.com/javase/9/intl/internationalization-enhancements-jdk-9.htm


j
jschnasse

对于当前 (2021-2) Java 版本,仍然存在旧的 ISO-8859-1 函数 utils.Properties#load

如果您使用 Properties.load,您必须使用 ISO-8859-1。

如果您使用 ResourceBundle,则 UTF-8 应该没问题。

请允许我引用官方文档。

属性资源包

PropertyResourceBundle 可以从 InputStream 或 Reader 构造,它表示一个属性文件。从 InputStream 构造 PropertyResourceBundle 实例要求输入流以 UTF-8 编码。默认情况下,如果在读取输入流时发生 MalformedInputException 或 UnmappableCharacterException,则 PropertyResourceBundle 实例将重置为异常之前的状态,重新读取 ISO-8859-1 中的输入流并继续读取。如果系统属性 java.util.PropertyResourceBundle.encoding 设置为“ISO-8859-1”或“UTF-8”,则输入流仅以该编码读取,如果遇到无效序列则抛出异常。如果指定了“ISO-8859-1”,则无法以 ISO-8859-1 编码表示的字符必须由 Java™ 语言规范第 3.3 节中定义的 Unicode Escapes 表示,而采用 Reader 的其他构造函数则没有有这个限制。此系统属性忽略其他编码值。初始化此类时会读取和评估系统属性。初始化后更改或删除属性无效。

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/PropertyResourceBundle.html

属性#load

从输入字节流中读取属性列表(键和元素对)。输入流采用 load(Reader) 中指定的简单的面向行的格式,并假定使用 ISO 8859-1 字符编码;也就是说,每个字节都是一个 Latin1 字符。非拉丁语1 中的字符和某些特殊字符使用Java™ 语言规范第3.3 节中定义的Unicode 转义表示在键和元素中。

https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/util/Properties.html#load(java.io.InputStream)