市面上读取 ini 的包都是 读取整个文件到内存中,再获取和修改值,
最后自己再调用保存文件, 这种方式在读取大文件的时候 非常的不友好.
windows api 中有现成的高效方法
安装 jna-platform
(里面封装了各个系统的 api ,直接用就行. 不用再手动写固定的函数定义)
jna-platform
是官方基于jna
封装好的 系统 API,jna
是调用自己写的 DLL
pom.xml
<dependency><groupId>net.java.dev.jna</groupId><artifactId>jna-platform</artifactId><version>5.14.0</version>
</dependency>
IniUtil.java
package org.example.myapp.utils;import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.platform.win32.Kernel32;
import com.sun.jna.platform.win32.Win32Exception;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.win32.W32APIOptions;import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;public class IniUtil {public static String getPrivateProfileString(final String lpAppName,final String lpKeyName, final String lpDefault,final String lpFileName) {final char buffer[] = new char[1024];Kernel32.INSTANCE.GetPrivateProfileString(lpAppName, lpKeyName,lpDefault, buffer, new WinDef.DWORD(buffer.length), lpFileName);return Native.toString(buffer);}public static void writePrivateProfileString(final String appName, final String keyName, final String string, final String fileName) {if (!Kernel32.INSTANCE.WritePrivateProfileString(appName, keyName, string, fileName))throw new Win32Exception(Kernel32.INSTANCE.GetLastError());}public static int getPrivateProfileInt(final String appName,final String keyName, final int defaultValue, final String fileName) {return Kernel32.INSTANCE.GetPrivateProfileInt(appName, keyName,defaultValue, fileName);}public static String[] getPrivateProfileSection(final String appName, final String fileName) {final char buffer[] = new char[32768];// Maximum INI file size according to MSDN (http://support.microsoft.com/kb/78346)if (Kernel32.INSTANCE.GetPrivateProfileSection(appName, buffer, new WinDef.DWORD(buffer.length), fileName).intValue() == 0) {throw new Win32Exception(Kernel32.INSTANCE.GetLastError());}return new String(buffer).split("\0");}public static String[] getPrivateProfileSectionNames(final String fileName) {final char buffer[] = new char[65536]; // Maximum INI file size according to MSDN (http://support.microsoft.com/kb/78346)if (Kernel32.INSTANCE.GetPrivateProfileSectionNames(buffer, new WinDef.DWORD(buffer.length), fileName).intValue() == 0) {throw new Win32Exception(Kernel32.INSTANCE.GetLastError());}return new String(buffer).split("\0");}public static void writePrivateProfileSection(final String appName, final String[] strings, final String fileName) {final StringBuilder buffer = new StringBuilder();for (final String string : strings)buffer.append(string).append('\0');buffer.append('\0');if (!Kernel32.INSTANCE.WritePrivateProfileSection(appName, buffer.toString(), fileName)) {throw new Win32Exception(Kernel32.INSTANCE.GetLastError());}}public interface MyKernel32 extends Library {/*** 用来写入 中文 (** @desc* jna-platform 5.14 源码中* Map<String, Object> DEFAULT_OPTIONS = Boolean.getBoolean("w32.ascii") ? ASCII_OPTIONS : UNICODE_OPTIONS;`* 在我的电脑上是 UNICODE_OPTIONS, 会导致 中文乱码 (编码是 gbk, 文件编码是 UTF-8))* 所以手动写这个函数定义 指定为 ASCII_OPTIONS (文件编码是 UTF-8, 中文正常显示 )*/MyKernel32 INSTANCE = Native.load("kernel32", MyKernel32.class, W32APIOptions.ASCII_OPTIONS);boolean WritePrivateProfileString(String lpAppName, String lpKeyName,String lpString, String lpFileName);}String _filePath;public IniUtil(String filePath) {_filePath = filePath;}public String readString(String sectionName, String keyName) {return getPrivateProfileString(sectionName, keyName, "", _filePath);}public String readString(String sectionName, String keyName, String defaultValue) {return getPrivateProfileString(sectionName, keyName, defaultValue, _filePath);}public Integer readInt(String sectionName, String keyName) {return getPrivateProfileInt(sectionName, keyName, 0, _filePath);}public Integer readInt(String sectionName, String keyName, Integer defaultValue) {return getPrivateProfileInt(sectionName, keyName, defaultValue, _filePath);}/*** 获取 sectionName 下所有的 keyName** @param sectionName* @return*/public ArrayList<String> readKeysBySectionName(String sectionName) {ArrayList<String> result = new ArrayList<>();final char buffer[] = new char[65536];WinDef.DWORD res = Kernel32.INSTANCE.GetPrivateProfileString(sectionName, null,null, buffer, new WinDef.DWORD(buffer.length), _filePath);int len = res.intValue();int j = 0;for (int i = 0; i < len; i++) {if (buffer[i] == 0) {String str = new String(buffer, j, i - j);result.add(str);j = i + 1;}}return result;}public ArrayList<String> readAllSectionNames() {ArrayList<String> result = new ArrayList<>();final char buffer[] = new char[65536];WinDef.DWORD res = Kernel32.INSTANCE.GetPrivateProfileString(null, null,null, buffer, new WinDef.DWORD(buffer.length), _filePath);int len = res.intValue();int j = 0;for (int i = 0; i < len; i++) {if (buffer[i] == 0) {String str = new String(buffer, j, i - j);result.add(str);j = i + 1;}}return result;}/*** 读取所有数据 ( !! 大文件慎用,会很慢, 小文件随意用)** @return*/public Dictionary<String, Dictionary<String, String>> readAllData() {Dictionary<String, Dictionary<String, String>> resultDictionary = new Hashtable<>();ArrayList<String> allSectionList = readAllSectionNames();for (String item : allSectionList) {ArrayList<String> allKeyList = readKeysBySectionName(item);Dictionary<String, String> dataDictionary = new Hashtable<>();for (String child : allKeyList) {String value = readString(item, child);dataDictionary.put(child, value);}resultDictionary.put(item, dataDictionary);}return resultDictionary;}/*** 新增 / 修改 (如果存在则修改,否则添加,没有文件会创建)** @param sectionName* @param keyName* @param value*/public void writeString(String sectionName, String keyName, String value) {// NOTE: jna-platform 封装的方法写入中文, 会乱码, 所以调用自己写的MyKernel32.INSTANCE.WritePrivateProfileString(sectionName, keyName, value, _filePath);}/*** 删除指定的 Section** @param sectionName*/public void deleteSection(String sectionName) {writePrivateProfileString(sectionName, null, null, _filePath);}/*** 删除所有内容*/public void deleteAllSection() {writePrivateProfileString(null, null, null, _filePath);}/*** 删除一个 key** @param sectionName* @param keyName*/public void deleteKey(String sectionName, String keyName) {writePrivateProfileString(sectionName, keyName, null, _filePath);}
}
测试一下
注入到 spring boot 中
package org.example.myapp.config;import org.example.myapp.utils.IniUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class IniConf {@Beanpublic IniUtil iniUtil() {String fileName = "D:\\Desktop\\demo.ini";return new IniUtil(fileName);}
}
调用
@Autowired
public IniUtil ini;@GetMapping("/test")
public Integer test() {// 测试自己封装的方法String s = ini.readString("colors", "background"); System.out.println(s);ini.writeString("General","test3","测试数据");//ini.deleteKey("General","test3");// 测试静态方法Integer ss= IniUtil.getPrivateProfileInt("colors","name",0,"D:\\Desktop\\demo.ini");return ss;
}