如何修复 ApplicationResources_fr.properties 损坏

How to fix ApplicationResources_fr.properties getting corrupted

我写的 class 有问题。 class 的目的是 add/remove/update applicationResource.properties 文件,<spring:message code="key" /> 使用这些文件为网站提供双语支持。手动与属性文件交互工作正常,但我有更大的需求,所以我构建了一种允许从数据库进行更改的方法。这给了我一个非常动态和灵活的系统,我可以从中工作。

但是,有一个问题。在某个时候,即使使用它进行一次更改后,法语字符也会发生变化。比如Déconnexion变成Déconnexion。在 notepad++ 中查看时,它的第一个 Déconnexion 然后损坏为 D\u00C3\u00A9connexion。此示例是原始属性文件的一部分。

原始(非临时)属性文件将 text file encoding 设置为 other: UTF-8。项目属性 text file encoding 设置为 inherited from container (Cp1252)。我尝试更改为 Other: UTF-8,但没有任何更改。

所以我的问题是,是什么导致我的法语字符损坏,我该如何解决? 我在下面提供了完整的 class .

更新: 在 StephaneM 的回答中提供帮助后,我能够准确找到导致损坏的原因,但尚未修复。 AR Class 中的 loadProperties() 函数。一旦加载了临时 AP 文件,法语字符就会损坏。这让我怀疑创建临时 AP 文件的原始过程使用了不同的编码。所以我将不得不追踪它。

package pojo;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;

/*
 * Purpose of this class is to handle all the     ApplicationResource(_fr).properties interactions
 * so that there is one unified location handling this, instead of code duplication.
 */

public class AR{
public final String en_path = "/ApplicationResources.properties";
public final String fr_path = "/ApplicationResources_fr.properties";

private Properties en_prop = null;
private Properties fr_prop = null;

public AR()
{
    loadProperties();
}
private void loadProperties()
{
    InputStream en_is = null;
    InputStream fr_is = null;
    try {
        this.en_prop = new Properties();
        this.fr_prop = new Properties();
        en_is = this.getClass().getResourceAsStream(en_path);
        fr_is = this.getClass().getResourceAsStream(fr_path);
        en_prop.load(en_is);
        fr_prop.load(fr_is);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
private boolean keyExist(String mykey, String mypath)   //deprecated due to better code/method
{
    Properties test_prop = null;
        InputStream is = null;
        try {
            test_prop = new Properties();

            is = this.getClass().getResourceAsStream(mypath);
            test_prop.load(is);
            Set<Object> keys = test_prop.keySet();
            for(Object k:keys) {
                String key = (String)k;
                //System.out.print(key + " ");
                if(key.equals(mykey))   
                {
                    return true;
                }
            }
            //System.out.println(" ");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    return false;
}
public boolean en_keyExist(String mykey)
{
    //searches english file
    loadProperties();
    return en_prop.containsKey(mykey);
    //return keyExist(mykey, en_path);  //original method
}
public boolean fr_keyExist(String mykey)
{
    //searches french file
    loadProperties();
    return fr_prop.containsKey(mykey);
    //return keyExist(mykey, fr_path);  //original method
}
public boolean en_fr_keyExist(String mykey)
{
    //searches both english and french files
    loadProperties();
    return (en_prop.containsKey(mykey) && fr_prop.containsKey(mykey));
    //return (keyExist(mykey, en_path) && keyExist(mykey, fr_path));    //original method
}
public String en_returnProperty(String mykey)
{
    //returns null if key does not exist
    loadProperties();
    return this.en_prop.getProperty(mykey);
}
public String fr_returnProperty(String mykey)
{
    //returns null if key does not exist
    loadProperties();
    return this.fr_prop.getProperty(mykey);
}
public void appendProperty(Properties new_en_prop,Properties new_fr_prop)
{
    //note: during a test, setProperty (used in populating the properties) does not allow duplicates, it overwrites.
    //So, load the existing properties, and for each new property add it

    loadProperties();
    for(Object key : new_en_prop.keySet())
    {
        en_prop.setProperty((String)key, new_en_prop.getProperty((String)key));
    }
    try (OutputStream en_os = new FileOutputStream(getClass().getResource(en_path).getFile(),false);)
    {
        en_prop.store(en_os, null);
    } catch (IOException e) {
        e.printStackTrace();
    }
    for(Object key : new_fr_prop.keySet())
    {
        fr_prop.setProperty((String)key, new_fr_prop.getProperty((String)key));
    }
    try (OutputStream fr_os = new FileOutputStream(getClass().getResource(fr_path).getFile(),false);)
    {
        fr_prop.store(fr_os, null);
    } catch (IOException e) {
        e.printStackTrace();
    }

}
public boolean appendProperty(String mykey, String en_val, String fr_val)   //appears to have timing error due to only saving last value
//due to timing error this function is only suitable for single additions
//due to the timing error, tried returning boolean to have it finished but was not successful
//setting the class variables to static did not solve the timing issue
{
    loadProperties();
    en_prop.setProperty(mykey, en_val);
    try (OutputStream en_os = new FileOutputStream(getClass().getResource(en_path).getFile(),false);)
    {
        en_prop.store(en_os, null);
    } catch (IOException e) {
        e.printStackTrace();
    }
    fr_prop.setProperty(mykey, fr_val);
    try (OutputStream fr_os = new FileOutputStream(getClass().getResource(fr_path).getFile(),false);)
    {
        fr_prop.store(fr_os, null);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return true;

}
public void en_setProperty(String mykey, String en_val)
//suspected timing issue, use only for singular changes
{
    loadProperties();
    en_prop.setProperty(mykey, en_val);
    try (OutputStream en_os = new FileOutputStream(getClass().getResource(en_path).getFile(),false);)
    {
        en_prop.store(en_os, null);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
public void fr_setProperty(String mykey, String fr_val)
//suspected timing issue, use only for singular changes
{
    loadProperties();
    fr_prop.setProperty(mykey, fr_val);
    try (OutputStream fr_os = new FileOutputStream(getClass().getResource(fr_path).getFile(),false);)
    {
        fr_prop.store(fr_os, null);
    } catch (IOException e) {
        e.printStackTrace();
    }
}
public void compareResources()
{
    Properties new_en = new Properties();
    Properties new_fr = new Properties();

    for(Object key : en_prop.keySet())
    {
        new_en.setProperty((String)key, en_prop.getProperty((String)key));
    }
    for(Object key : fr_prop.keySet())
    {
        new_fr.setProperty((String)key, fr_prop.getProperty((String)key));
    }

    Properties temp = (Properties) new_en.clone();

    for(Object key : temp.keySet())
    {
        if(new_fr.containsKey((String) key))
        {
            new_fr.remove(key);
            new_en.remove(key);
        }
    }

    for(Object key : new_en.keySet())
    {
        System.out.println("English only key: " + ((String)key));
    }
    for(Object key : new_fr.keySet())
    {
        System.out.println("French only key: " + ((String)key));
    }
}   

}

class 的示例用例,直接取自应用程序,但进行了一些编辑,因此此处仅包含相关部分

AR testing = new AR();
Properties en_prop = new Properties();
Properties fr_prop = new Properties();

final String test_prod_cur = "{call BILINGUAL_VALUES(?)}";
ResultSet rs = null;
try     ( 
  Connection connection = jdbcTemplate.getDataSource().getConnection();
  CallableStatement callableStatement = connection.prepareCall(test_prod_cur);
)
{
    callableStatement.registerOutParameter(1, OracleTypes.CURSOR);
    callableStatement.executeUpdate();
    rs = (ResultSet) callableStatement.getObject(1);
    while (rs.next())
    {
        String thead = rs.getString(1);
        en_prop.setProperty(keyheader+thead, rs.getString(2));
        fr_prop.setProperty(keyheader+thead, rs.getString(3));
        //testing.appendProperty(keyheader+thead, rs.getString(2), rs.getString(3));    //has a timing issue, ends up only appending final value
   }
}
catch (SQLException e)
{
System.out.println("SQLException - bilingual values");
System.out.println(e.getMessage());
}       
testing.appendProperty(en_prop, fr_prop);

关于这个问题:"what is causing the corruption to my French characters and how can I fix it?",答案在文档中(Properties.store()):

public void store(OutputStream out, String comments) throws IOException

Writes this property list (key and element pairs) in this Properties table to the output stream in a format suitable for loading into a Properties table using the load(InputStream) method.

Properties from the defaults table of this Properties table (if any) are not written out by this method.

This method outputs the comments, properties keys and values in the same format as specified in store(Writer), with the following differences:

  • The stream is written using the ISO 8859-1 character encoding.
  • Characters not in Latin-1 in the comments are written as \uxxxx for their appropriate unicode hexadecimal value xxxx.
  • Characters less than \u0020 and characters greater than \u007E in property keys or values are written as \uxxxx for the appropriate hexadecimal value xxxx.

我不是唯一遇到过这个问题的人,我设法找到了另一个问题,它是 answers that lead me to my solution. I have to thank another site 让我知道要包含哪些内容的问题之一。

加改改的只有四行,我先列出来,再给出一个完整的功能。

import java.io.Reader;
Reader reader = new InputStreamReader(fr_is, "UTF-8");
fr_prop.load(reader); //instead of fr_prop.load(fr_is);
reader.close();

功能齐全

import java.io.Reader;

private void loadProperties()
{
    InputStream en_is = null;
    InputStream fr_is = null;
    try {
        this.en_prop = new Properties();
        this.fr_prop = new Properties();
        en_is = this.getClass().getResourceAsStream(en_path);
        fr_is = this.getClass().getResourceAsStream(fr_path);

        Reader reader = new InputStreamReader(fr_is, "UTF-8");

        en_prop.load(en_is);
        fr_prop.load(reader);
        reader.close();

    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

通过引入 reader 并使用它,它已经清除了法语字符损坏。

我应该提一下,在进行上述更改并使其正常工作之前,我更改了我能找到的每个文件 属性 到 UTF-8。这个 site gives you the changes I made. This was a nice page 帮助我确认了编码。