凡是中文乱码问题,都是字符读写的编码格式不统一的问题。
一般情况下,便于统一字符编码格式,所有文件读写格式均设为utf-8模式,这些地方包括数据库字符存储格式,源代码文件编码格式,资源文件编码格式,前端页面编码格式,网络IO流字符编码格式等。
鉴于以上前提,本文着重关心的是properties资源文件的读写。下面所展示的封装类功能包括:
(1)读取资源文件中的值(处理了同一个资源文件中资源值相互引用的问题)
(2)写资源文件
(3)处理了properties文件中中文字符读取乱码问题。
关于问题(1),例如对于具有以下内容的properties文件:在test1的值中通过自定义的语法${key}来引用同一文件中的其他键值。在处理过程中,先读取字符串test1的值,再通过正则表达式匹配出key值,再按key值读取test的值,并拼凑到test1中,从而得到最终的test1的值。
test=test
test1=${test}
关于问题2,重点在于如果便捷地根据文件名获取到文件输入流,采用的方式是通过类ClassLoader根据编译后相对于classpath的路径名,获取到文件的URL,再转化为URI,由于File函数的构造函数中,提供了以文件URI为输入参数的构造器,所以便可以直接通过这种方式获得File对象,进而获得对应资源文件的输入流。
需要指出的是,在以下两种获得文件URL的方式中,区别仅在于是否通过ClassLoader,其中方式1适用于任何Java项目环境下;而方式2则仅适用于Java Application,而在Java Web应用环境下,获取不到文件URL值。
//方式1
URL url = PropsUtil.class.getClassLoader().getResource("setting/token.properties");
//方式2
URL url = PropsUtil.class..getResource("setting/token.properties");
对于问题3,方便起见,首先需将对应properties文件设置为utf-8编码格式(在IDEA环境下,由file->setting->editor->file encodings设置),否则以unicode编码的话,不仅不便于文档的统一,而且里面的中文字符会自动转成Unicode格式,极其不直观。
通常获得Properties对象的方式是,通过文件ClassLoader(方式1)获得文件输入流,在调用properties.load(imputstream)的方式完成初始化,然而这种方式下,由于不能够指定读取资源文件时的字符编码格式,在读取中文字符时便会出现乱码。
所以,更好的做法是,将该inputstream作为构造参数包装成Reader类,并在构造器的另一个参数中指明采用utf-8的方式,再通过properties.load(reader),完成初始化,便可解决中文字符读取乱码问题。
类源码如下:
/**
* Created by Song on 2016/11/2.
* mail:1147649695@qq.com
* @since v0.0
*/
public final class PropsUtil {
private static final Pattern PATTERN = Pattern.compile("\\$\\{([^\\}]+)\\}");
/**
* 加载资源文件
* @param fileName 资源文件名
* @return
*/
public static Properties loadProps(String fileName){
Properties pros = null;
Reader reader = null;
try{
reader = new InputStreamReader(Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName),"utf-8");
if(null == reader){
throw new FileNotFoundException(fileName+" file is not found");
}
pros = new Properties();
pros.load(reader);
}catch (IOException e){
System.err.println("load peoperties file failed");
e.printStackTrace();
}finally {
if(null != reader){
try {
reader.close();
}catch (IOException e){
System.err.println("close input stream failed");
e.printStackTrace();
}
}
}
return pros;
}
/**
* 获取资源文件值
* @param key 键名
* @param prop 资源文件句柄
* @return
*/
public static String getProperty(String key,Properties prop) {
String value = prop.getProperty(key);
Matcher matcher = PATTERN.matcher(value);
StringBuffer buffer = new StringBuffer();
while (matcher.find()) {
String matcherKey = matcher.group(1);
String matchervalue = prop.getProperty(matcherKey);
if (matchervalue != null) {
matcher.appendReplacement(buffer, matchervalue);
}
}
matcher.appendTail(buffer);
return buffer.toString();
}
/**
* 写资源文件
* 问题很奇怪,重新加载资源文件并读键值,可得到最新的值,然而直接打开文件,却读不到新值
* @param key
* @param value
* @param fileName
* @return
* @throws Exception
*/
public static boolean setProperty(String key,String value,String fileName) throws Exception{
URL url = PropsUtil.class.getClassLoader().getResource("setting/token.properties");
Properties properties = new Properties();
properties.setProperty(key, value);
properties.store(new FileOutputStream(new File(url.toURI())),"");
return true;
}
}