当前位置: 首页 > news >正文

重写Properties类,实现对properties文件的有序读写,数据追加,解决中文乱码

前言

        *.properties文件,是 Java 支持的一种配置文件类型,并且 Java 提供了 properties 类来读取 properties 文件中的信息。文件中以键值对 "键=值"的形式,存储工程中会多次重复使用的配置信息,通过“Properties”类来读取这些信息,以实现“一次编写,多处调用;需要修改配置文件时,只修改一处即可”的效果

        本文介绍【读写 properties 文件】,【追加数据】,【实现有序读写】,【解决中文乱码】问题。


正文

一、Properties 类简介

        Properties有一个特殊的作用,专门用来加载xxx.properties配置文件。Properties继承于Hashtable,表示了一个持久的属性集,可保存在流中或从流中加载。属性列表中,每个键及其对应值都是一个字符串。

  • 常用方法: 
方法名含义
public String getProperty(String key)用指定的键在此属性列表中搜索属性

public Object setProperty(String key,String value)

向Properties中增加属性,有则覆盖,没有则新增
public void load(InputStream in)从输入流中读取属性列表(键和元素对)
public void load(Reader reader)按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)
public void store(OutputStream out, String comments)将此 Properties 表中的属性列表(键和元素对)写入输出流
public void store(Writer writer, String comments)将此 Properties 表中的属性列表(键和元素对)写入输出字符


二、读写 properties 文件

        由于Properties继承于Hashtable,所以,当新增属性写入到 .properties 文件的时候,结果显示的顺序可能并不是我们当初set属性的顺序。这个问题要注意,我们再后面会解决。

  • Properties继承于Hashtable

  • 借助IO流,利用Properties类操作.properties文件
    // .properties文件的相对路径private static final String FILE_ADDRESS = "src/main/resources/application.properties";@GetMapping("/test")public String test()  {log.info("test 接口调用:【开始】 --------------------");this.writeIntoText(new Properties());Properties prop = this.readFromText();log.info("jdbc.username = " + properties.getProperty("jdbc.username"));log.info("test 接口调用:【结束】 --------------------");return "success";}/*** 模拟向 properties 文件写入数据*/private void writeIntoText(Properties properties){OutputStream output = null;try {output = new FileOutputStream(FILE_ADDRESS);properties.setProperty("jdbc.driver", "com.mysql.jdbc.Driver");properties.setProperty("jdbc.url", "jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8");properties.setProperty("jdbc.username", "root");properties.setProperty("jdbc.password", "123456");properties.store(output, "tjm modify");} catch (IOException io) {io.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 读取 properties 文件,返回 Properties 对象*/private Properties readFromText(){Properties properties = new Properties();InputStream input = null;try {input = new FileInputStream(FILE_ADDRESS);properties.load(input);} catch (IOException io) {io.printStackTrace();} finally {if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}return properties;}
  • 运行上述代码会产生几个问题:
  1. 顺序是无序的,或者说是hash排序的,并不是set的顺序;
  2. 每次write,properties文件的数据都会被覆盖掉,只展示最新数据;
  3. 如果value里面包含中文,那一定会是乱码;

        下面,我们就以此解决这几个问题。


三、解决追加、有序、乱码问题

2.1 实现有序读写

        自定义一个PropertiesUtil类,该类继承自Properties。PropertiesUtil提供一个返回由key按照存入顺序组成的List的方法,getKeyList(),这样就可以解决有序写的问题。至于读嘛,也是一样的道理。

  • 如何保证getKeyList()方法返回的就是有序的key组成的集合?

        通过查询Properties方法的源码,发现:Properties.load()方法从.properties文件加载内容时,是按照文件顺序进行读取,而Properties.setProperty()方法底层调用的是父类HashTable.put()方法进行储存。

        HashTable本身就是无序的,所以,解决办法就是让PropertiesUtil持有一个私有的可以有序存储key的集合,然后重写父类的put()方法,在方法体中照常通过super.put()进行属性的存储,同时将key添加到存储key的集合中。

        再查源码,发现: Properties将当前对象的内容存放到指定的输出流的方法又2个,save()和store(),但是它们的底层逻辑都是一样的,都是通过调用Properties.keys()方法获取一个Enumeration,然后对该Enumeration进行遍历,依次将对应的key和value写入到输出流中。要保证写入是有序的,就要保证遍历keys()方法返回的Enumeration取出的元素key是有序的。

        所以,得到最终的解决方法是:重写keys()方法,保证遍历Enumeration得到的key是有序的。

  • PropertiesUtil类的完整代码如下:
import java.io.*;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Properties;/*** @Description: Java Punk* @Created: by JavaPunk on 2023/5/10 16:33* @Version: 1.0.0*/
public class PropertiesUtil extends Properties {private static final long serialVersionUID = 1L;private List<Object> keyList = new ArrayList<Object>();/*** 默认构造方法*/public PropertiesUtil() {}/*** 从指定路径加载信息到Properties* @param path*/public PropertiesUtil(String path) {try {InputStream is = new FileInputStream(path);this.load(is);} catch (FileNotFoundException e) {e.printStackTrace();throw new RuntimeException("指定文件不存在!");} catch (IOException e) {e.printStackTrace();}}/*** 重写put方法,按照property的存入顺序保存key到keyList,遇到重复的后者将覆盖前者。*/@Overridepublic synchronized Object put(Object key, Object value) {this.removeKeyIfExists(key);keyList.add(key);return super.put(key, value);}/*** 重写remove方法,删除属性时清除keyList中对应的key。*/@Overridepublic synchronized Object remove(Object key) {this.removeKeyIfExists(key);return super.remove(key);}/*** keyList中存在指定的key时则将其删除*/private void removeKeyIfExists(Object key) {keyList.remove(key);}/*** 获取Properties中key的有序集合* @return*/public List<Object> getKeyList() {return keyList;}/*** 保存Properties到指定文件,默认使用UTF-8编码* @param path 指定文件路径*/public void store(String path) {this.store(path, "UTF-8");}/*** 保存Properties到指定文件,并指定对应存放编码* @param path 指定路径* @param charset 文件编码*/public void store(String path, String charset) {if (path != null && !"".equals(path)) {try {OutputStream os = new FileOutputStream(path);BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os, charset));this.store(bw, null);bw.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}} else {throw new RuntimeException("存储路径不能为空!");}}/*** 重写keys方法,返回根据keyList适配的Enumeration,且保持HashTable keys()方法的原有语义,* 每次都调用返回一个新的Enumeration对象,且和之前的不产生冲突*/@Overridepublic synchronized Enumeration<Object> keys() {return new EnumerationAdapter<Object>(keyList);}/*** List到Enumeration的适配器*/private class EnumerationAdapter<T> implements Enumeration<T> {private int index = 0;private final List<T> list;private final boolean isEmpty;public EnumerationAdapter(List<T> list) {this.list = list;this.isEmpty = list.isEmpty();}public boolean hasMoreElements() {// isEmpty的引入是为了更贴近HashTable原有的语义,在HashTable中添加元素前调用其keys()方法获得一个Enumeration的引用,// 之后往HashTable中添加数据后,调用之前获取到的Enumeration的hasMoreElements()将返回false,但如果此时重新获取一个// Enumeration的引用,则新Enumeration的hasMoreElements()将返回true,而且之后对HashTable数据的增、删、改都是可以在// nextElement中获取到的。return !isEmpty && index < list.size();}public T nextElement() {if (this.hasMoreElements()) {return list.get(index++);}return null;}}
}

2.2 实现数据追加

        在上面【读写 properties 文件】中简单介绍了写入的方法,因为每次都会 new Properties(),所以每次都会把源数据全部覆盖掉。清楚了原因,自然也就找到了解决办法:写入之前先将源文件加载出来,再进行有序的追加。

  • 源码:先加载输入流,随后set新属性,最后store输出流
    /*** 模拟向 properties 文件追加写入数据*/private void addToText(){// 将 PropertiesUtil 换成重写前的 Properties 类,最后写入的顺序是hash排序的// Properties properties = new Properties();PropertiesUtil properties = new PropertiesUtil();InputStream input = null;OutputStream output = null;try {// 先用输入流加载.properties文件input = new BufferedInputStream(new FileInputStream(FILE_ADDRESS));properties.load(new InputStreamReader(input));// 输出流(FileOutputStream)对象,必须在Properties类加载(load)完以后创建(new)output = new FileOutputStream(FILE_ADDRESS);properties.setProperty("jdbc2.username", "PropertiesUtil Orderly test");properties.store(output, "tjm modify");} catch (IOException io) {io.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}}
  • PropertiesUtil运行结果:

  •  Properties运行结果:

2.3 解决中文乱码

        上面测试使用的都是中文,如果参数中包含【中文】,就会出现乱码问题,解决办法:输入输出时,设置【charsetName = "utf-8"】。

  • 代码展示:
    /*** 模拟向 properties 文件追加写入数据*/private void addToText(){// 将 PropertiesUtil 换成重写前的 Properties 类,最后写入的顺序是hash排序的
//         Properties properties = new Properties();PropertiesUtil properties = new PropertiesUtil();InputStream input = null;OutputStream output = null;try {// 先用输入流加载.properties文件input = new BufferedInputStream(new FileInputStream(FILE_ADDRESS));properties.load(new InputStreamReader(input, "utf-8"));// 输出流(FileOutputStream)对象,必须在Properties类加载(load)完以后创建(new)output = new FileOutputStream(FILE_ADDRESS);properties.setProperty("jdbc3.username", "PropertiesUtil 有序测试");properties.store(new OutputStreamWriter(output, "utf-8"), "tjm modify");} catch (IOException io) {io.printStackTrace();} finally {if (output != null) {try {output.close();} catch (IOException e) {e.printStackTrace();}}if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}}/*** 读取 properties 文件,返回 Properties 对象*/private Properties readFromText(){PropertiesUtil properties = new PropertiesUtil();InputStream input = null;try {input = new FileInputStream(FILE_ADDRESS);properties.load(new InputStreamReader(input, "utf-8"));log.info("举例说明:jdbc3.username = " + properties.getProperty("jdbc3.username"));} catch (IOException io) {io.printStackTrace();} finally {if (input != null) {try {input.close();} catch (IOException e) {e.printStackTrace();}}}return properties;}
  • 结果展示:


四、PropertiesConfiguration介绍

        PropertiesConfiguration 是 Apache 帮我们实现按照文件的顺序读取properties文件的类,Properties类能做的它都能做。不仅如此,他还有许多方便实用的附加功能。

  • 示例代码:功能展示
    /*** 模拟向 properties 文件写入数据*/private void setParam(){try {PropertiesConfiguration propsConfig =  new PropertiesConfiguration(FILE_ADDRESS);propsConfig.setEncoding("utf-8");// 修改属性之后自动保存,省去了propsConfig.save()过程propsConfig.setAutoSave(true);// setProperty:遇到同名key会替换value,没有则新增propsConfig.setProperty("set.name", "123456");// addProperty:只会新增,即使遇到遇到同名key也不会替换propsConfig.addProperty("add.name", "456789");}catch (Exception ex) {ex.printStackTrace();}}/*** 模拟向 properties 文件读取数据*/private void getParam(){try {PropertiesConfiguration propsConfig =  new PropertiesConfiguration(FILE_ADDRESS);propsConfig.setEncoding("utf-8");String username = propsConfig.getString("jdbc.username");log.info("举例说明:jdbc.username = " + username);}catch (Exception ex) {ex.printStackTrace();}}

相关文章:

重写Properties类,实现对properties文件的有序读写,数据追加,解决中文乱码

前言 *.properties文件&#xff0c;是 Java 支持的一种配置文件类型&#xff0c;并且 Java 提供了 properties 类来读取 properties 文件中的信息。文件中以键值对 "键值"的形式&#xff0c;存储工程中会多次重复使用的配置信息&#xff0c;通过“Properties”类来读…...

态势感知与信质、信量

未来的新智能是人机环境系统智能&#xff0c;而人机融合的态势感知是其关键&#xff0c;简单地说&#xff0c;态势感知&#xff08;situation awareness&#xff09;就是智能体在“一定时间和空间环境中的元素的感知&#xff0c;对它们的含义的理解&#xff0c;并对他们稍后状态…...

20230508----重返学习-call()与bind()重写-JS中数据类型检测汇总-装箱与拆箱-类的多种继承方案

day-065-sixty-five-20230508-call()与bind()重写-JS中数据类型检测汇总-装箱与拆箱-类的多种继承方案 call()与bind()重写 call()重写 call()的作用例子 let obj {name: 珠峰培训 } const fn function fn(x, y, ev) {console.log(this, x, y, ev)return x y } let res f…...

Node.js对ES6 及更高版本的支持

目录 1、简介 2、默认情况下什么特性随着 Node.js 一起发布&#xff1f; 3、有哪些特性在开发中&#xff1f; 4、移除这个标记&#xff08;--harmony&#xff09;吗 5、Node.js 对应 V8 引擎 1、简介 Node.js 是针对 V8 引擎构建的。通过与此引擎的最新版本保持同步&…...

【华为OD机试2023】工作安排 100% C++ Java Python

【华为OD机试2023】工作安排 100% C++ Java Python 前言 如果您在准备华为的面试,期间有想了解的可以私信我,我会尽可能帮您解答,也可以给您一些建议! 本文解法非最优解(即非性能最优),不能保证通过率。 Tips1:机试为ACM 模式 你的代码需要处理输入输出,input/cin接收…...

面试题Spring - 关于Spring的25个经典问题和答案

文章目录 1 什么是Spring框架&#xff1f;Spring框架有哪些主要模块&#xff1f;2 使用Spring框架能带来哪些好处&#xff1f;3 什么是控制反转(IOC)&#xff1f;什么是依赖注入&#xff1f;4 请解释下Spring框架中的IoC&#xff1f;5 BeanFactory和ApplicationContext有什么区…...

C++学习day--10 条件判断、分支

1、if语句 if 语句的三种形态 形态1&#xff1a;如果。。。那么。。。 #include <iostream> using namespace std; int main( void ) { int salary; cout << " 你月薪多少 ?" ; cin >> salary; if (salary < 20000) { cout <&…...

和月薪5W的聊过后,才发现自己一直在打杂···

前几天和一个朋友聊面试&#xff0c;他说上个月同时拿到了腾讯和阿里的offer&#xff0c;最后选择了阿里。 我了解了下他的面试过程&#xff0c;就一点&#xff0c;不管是阿里还是腾讯的面试&#xff0c;这个级别的程序员&#xff0c;都会考察项目管理能力&#xff0c;并且权重…...

SSM框架学习-AOP通知类型

在AOP中&#xff0c;通知&#xff08;Advice&#xff09;是对切点进行操作的方法&#xff0c;用于实现切面定义的具体逻辑。Spring框架支持五种类型的通知&#xff1a; 1. 前置通知&#xff08;Before advice&#xff09; 在连接点执行前&#xff0c;执行通知 Before("**…...

微信小程序原生开发功能合集十四:登录健权及注册功能实现

本章实现微信自动登录及注册修改功能,包括匿名账号生成、权限自动检测、注册界面及注册流程的实现。   另外还提供小程序开发基础知识讲解课程,包括小程序开发基础知识、组件封装、常用接口组件使用及常用功能实现等内容,具体如下:    1. CSDN课程: https://edu.csdn…...

【Java零基础入门篇】第 ⑤ 期 - 抽象类和接口(二)

博主&#xff1a;命运之光 专栏&#xff1a;Java零基础入门 学习目标 1.了解什么是抽象类&#xff0c;什么是接口&#xff1b; 2.掌握抽象类和接口的定义方法&#xff1b; 3.理解接口和抽象类的使用场景&#xff1b; 4.掌握多态的含义和用法&#xff1b; 5.掌握内部类的定义方法…...

Halcon 集合运算(差集difference、交集intersection、并集union2、打散connection与 合集 union1)

文章目录 1 差集difference2. 交集intersection3. 并集union24 打散connection与 合集 union1 (二者互为反义词)4.1 打散connection与4.2 合集 union1 (注意与交集的区别)5 示例原图1 差集difference difference (Operator) Name difference — Calculate the difference …...

Allegro约束规则设计

首先是物理规则。 然后是间距规则。 如果有些特殊要求&#xff0c;还需要设计电气规则。 原则上&#xff0c;把规则设计好&#xff0c;然后把规则赋值给网络。 物理规则。PCS。 对于名字为DEFAULT的PCS&#xff0c;这是最基础的整板默认规则。 没有特殊要求的网络&#xff0c…...

PyQt5桌面应用开发(11):摸鱼也要讲基本法之桌面精灵

本文目录 PyQt5桌面应用系列鼠标不要钱&#xff0c;手腕还不要钱吗&#xff1f;PyQt5源程序python文件资源定义界面定义文件 技术要素资源文件StyleSheetsQMainWindow设置窗体几何 结论 PyQt5桌面应用系列 PyQt5桌面应用开发&#xff08;1&#xff09;&#xff1a;需求分析 PyQ…...

Talk预告 | 大连理工大学IIAU Lab在读博士生严彬:走向通用实例感知

本期为TechBeat人工智能社区第495期线上Talk&#xff01; 北京时间5月10日(周三)20:00&#xff0c;大连理工大学IIAU Lab在读博士生—严彬的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “走向通用实例感知”&#xff0c;届时将介绍和探讨通用实…...

2023-05-04 LeetCode每日一题(摘水果)

2023-05-04每日一题 一、题目编号 2106. 摘水果二、题目链接 点击跳转到题目位置 三、题目描述 在一个无限的 x 坐标轴上&#xff0c;有许多水果分布在其中某些位置。给你一个二维整数数组 fruits &#xff0c;其中 fruits[i] [positioni, amounti] 表示共有 amounti 个水…...

[工具]Pytorch-lightning的使用

Pytorch-lightning的使用 Pytorch-lightning介绍Pytorch-lightning与Pytorch的区别Pytorch-lightning框架的优势Pytorch-lightning框架 重要资源 Pytorch-lightning介绍 这里介绍Pytorch_lighting框架. Pytorch-lightning与Pytorch的区别 Pytorch-lightning可以简单的看作是…...

互联网摸鱼日报(2023-05-09)

互联网摸鱼日报&#xff08;2023-05-09&#xff09; InfoQ 热门话题 面向数字化提质提效的低代码架构设计 | 低代码技术内幕 提升字节规模化效能的平台化思路 &#xff5c; 极客有约 从微服务转为单体架构、成本降低 90%&#xff0c;亚马逊内部案例引发轰动&#xff01;CTO&…...

MySQL常见的存储引擎

InnoDB&#xff1a;InnoDB是一种兼顾高可靠性和高性能的通用存储引擎&#xff0c;在MySQL 5.5之后&#xff0c;InnoDB是默认的MySQL存储引擎。 特点&#xff1a;1、DML操作遵循ACID模型&#xff0c;支持事务; 2、行级锁&#xff0c;提高并发访问性能; 3、支持外键FOREIGN KEY约…...

迅为i.MX6ULL开发板生成 KEY 文件,并安装

使用“ssh-keygen” 生成个四个 key 文件“ssh_host_rsa_key” “ssh_host_dsa_key” “ssh_host_ecdsa_key” 和“ssh_host_ed25519_key” 。 1 在虚拟机 Ubuntu 控制台&#xff0c; “ /home/ssh/openssh-4.6p1” 目录下&#xff0c; 使用命 令“ssh-keygen -t rsa -f ssh…...

C++实现分布式网络通信框架RPC(3)--rpc调用端

目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中&#xff0c;我们已经大致实现了rpc服务端的各项功能代…...

Linux 文件类型,目录与路径,文件与目录管理

文件类型 后面的字符表示文件类型标志 普通文件&#xff1a;-&#xff08;纯文本文件&#xff0c;二进制文件&#xff0c;数据格式文件&#xff09; 如文本文件、图片、程序文件等。 目录文件&#xff1a;d&#xff08;directory&#xff09; 用来存放其他文件或子目录。 设备…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

K8S认证|CKS题库+答案| 11. AppArmor

目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作&#xff1a; 1&#xff09;、切换集群 2&#xff09;、切换节点 3&#xff09;、切换到 apparmor 的目录 4&#xff09;、执行 apparmor 策略模块 5&#xff09;、修改 pod 文件 6&#xff09;、…...

反向工程与模型迁移:打造未来商品详情API的可持续创新体系

在电商行业蓬勃发展的当下&#xff0c;商品详情API作为连接电商平台与开发者、商家及用户的关键纽带&#xff0c;其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息&#xff08;如名称、价格、库存等&#xff09;的获取与展示&#xff0c;已难以满足市场对个性化、智能…...

中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试

作者&#xff1a;Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位&#xff1a;中南大学地球科学与信息物理学院论文标题&#xff1a;BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接&#xff1a;https://arxiv.…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

微信小程序 - 手机震动

一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注&#xff1a;文档 https://developers.weixin.qq…...

Mobile ALOHA全身模仿学习

一、题目 Mobile ALOHA&#xff1a;通过低成本全身远程操作学习双手移动操作 传统模仿学习&#xff08;Imitation Learning&#xff09;缺点&#xff1a;聚焦与桌面操作&#xff0c;缺乏通用任务所需的移动性和灵活性 本论文优点&#xff1a;&#xff08;1&#xff09;在ALOHA…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...