Jackcess:MSAccess 数据库的错误字符集
Jackcess: wrong charset for MSAccess database
我有一个 MS-Access 数据库,其中包含 "encrypted" 个字符串。这些看起来像这样:
但是,我很快注意到这些字符串的长度与明文的长度完全匹配(我知道明文)。因此,通过 Excel 的一些尝试,我发现如果您使用 =CODE(<char>)
-函数(这样您就可以在默认字符集中获得字符代码,而 =CHAR(<number>)
反之亦然)和将此数字与该符号应代表您的字母的字符代码相异或 总是 得到相同的结果。这意味着我只需要在 java 中创建一个包含这些值的数组,瞧。 Excel 例子(右边提到的"Array"):
示例:“>>”的索引为 (dec) 187,因此 187xor253 产生 70 => "F"
现在,我使用 jackcess 来访问这些值,"decryption" 基本上没问题,但有时我会从字符串中得到错误的字符。在 Excel 中一切正常。最佳结果代码:
public static final int[] DECRYPT_KEY = { 253, 203, 204, 217, 226, 205, 128, 201, 222, 183, 58, 217, 230, 201, 183, 211, 158, 203, 167, 213, 35, 33, 201, 123, 186, 247 };
public static void main(String[] args) throws IOException
{
System.out.println(System.getProperty("file.encoding"));
Database db = DatabaseBuilder.open(new File("/home/***/TM.db"));
Table table = db.getTable("personal");
for (Row row : table)
{
String vorname = row.getString("vorname");
byte[] vornameArr = vorname.getBytes("cp1252");
for (int i1 = 0; i1 < vornameArr.length; i1++)
{
vornameArr[i1] = (byte) ((vornameArr[i1] & 0xff) ^ DECRYPT_KEY[i1]);
}
System.out.println(new String(vornameArr, "cp1252"));
}
}
但是正如我所说,有些字符仍然是错误的,在 Excel 但是一切都很好。当我打印出 getBytes("cp1272")
给出的数字时,它与 Excel 完全不同。
你有什么想法,我可能做错了什么,为什么 java 有时给出的值与 Excel 如此不同?什么是更好的方法?我已经尝试了所有字符集的组合,有些在其他失败的地方工作,但后来又出现了其他错误的结果。
我能够通过使用您问题中的字节值破解数据库文件来重现您的问题。行
byte[] vornameArr = vorname.getBytes("cp1252");
尝试将vorname
字符转换为cp1252字节,但是没有对应U+008F(十进制143,SINGLE SHIFT THREE)的cp1252字符,所以Java转换那个字符到问号 (0x3F)。因此,您的解码步骤是解码 0x3F 而不是 0x8F,这就是为什么您得到 "FadiÝa" 而不是 "Fadima"。
通过将上面的单行替换为
,我能够得到正确的结果
byte[] doubleBytes = vorname.getBytes("UTF-16LE"); // 187 0 170 0 168 0 ...
byte[] vornameArr = new byte[doubleBytes.length / 2];
for (int i = 0; i < vornameArr.length; i++) {
vornameArr[i] = doubleBytes[i * 2]; // remove nulls
}
然后 运行 vornameArr
字节通过你的解码循环。 (如果愿意,您也可以在上述循环中应用解码转换。)
感谢@Gord Thompson 和他建议的网站 (fileformat.info) 我终于找到了答案:有时字符看起来很相似,并且出于某种原因在数据库中首选 "higher" (例如 unicode 字符 402 和 131)。我的 java 代码希望所有内容都具有较低的值,正如 excel 提供的那样。因此,如果代码高于 255,则需要将其替换为较低的值。出于某种原因,getBytes("cp1252")
将始终 return 较低的值,但是 toCharArray()
和 getBytes("UTF-16LE")
将 return 较高的正确值(比较:fileformat 192)
所以我的代码现在是这样的并且完美运行:
String vorname = row.getString("vorname");
char[] vornameArr = vorname.toCharArray();
for (int i = 0; i < vornameArr.length; i++)
{
if (vornameArr[i] > 255)
{
vornameArr[i] = (char) (String.valueOf(vornameArr[i]).getBytes("cp1252")[0] & 0xff);
}
vornameArr[i] = (char) (vornameArr[i] ^ DECRYPT_KEY[i]);
}
System.out.println(String.valueOf(vornameArr));
非常感谢您的帮助!
我有一个 MS-Access 数据库,其中包含 "encrypted" 个字符串。这些看起来像这样:
但是,我很快注意到这些字符串的长度与明文的长度完全匹配(我知道明文)。因此,通过 Excel 的一些尝试,我发现如果您使用 =CODE(<char>)
-函数(这样您就可以在默认字符集中获得字符代码,而 =CHAR(<number>)
反之亦然)和将此数字与该符号应代表您的字母的字符代码相异或 总是 得到相同的结果。这意味着我只需要在 java 中创建一个包含这些值的数组,瞧。 Excel 例子(右边提到的"Array"):
现在,我使用 jackcess 来访问这些值,"decryption" 基本上没问题,但有时我会从字符串中得到错误的字符。在 Excel 中一切正常。最佳结果代码:
public static final int[] DECRYPT_KEY = { 253, 203, 204, 217, 226, 205, 128, 201, 222, 183, 58, 217, 230, 201, 183, 211, 158, 203, 167, 213, 35, 33, 201, 123, 186, 247 };
public static void main(String[] args) throws IOException
{
System.out.println(System.getProperty("file.encoding"));
Database db = DatabaseBuilder.open(new File("/home/***/TM.db"));
Table table = db.getTable("personal");
for (Row row : table)
{
String vorname = row.getString("vorname");
byte[] vornameArr = vorname.getBytes("cp1252");
for (int i1 = 0; i1 < vornameArr.length; i1++)
{
vornameArr[i1] = (byte) ((vornameArr[i1] & 0xff) ^ DECRYPT_KEY[i1]);
}
System.out.println(new String(vornameArr, "cp1252"));
}
}
但是正如我所说,有些字符仍然是错误的,在 Excel 但是一切都很好。当我打印出 getBytes("cp1272")
给出的数字时,它与 Excel 完全不同。
你有什么想法,我可能做错了什么,为什么 java 有时给出的值与 Excel 如此不同?什么是更好的方法?我已经尝试了所有字符集的组合,有些在其他失败的地方工作,但后来又出现了其他错误的结果。
我能够通过使用您问题中的字节值破解数据库文件来重现您的问题。行
byte[] vornameArr = vorname.getBytes("cp1252");
尝试将vorname
字符转换为cp1252字节,但是没有对应U+008F(十进制143,SINGLE SHIFT THREE)的cp1252字符,所以Java转换那个字符到问号 (0x3F)。因此,您的解码步骤是解码 0x3F 而不是 0x8F,这就是为什么您得到 "FadiÝa" 而不是 "Fadima"。
通过将上面的单行替换为
,我能够得到正确的结果byte[] doubleBytes = vorname.getBytes("UTF-16LE"); // 187 0 170 0 168 0 ...
byte[] vornameArr = new byte[doubleBytes.length / 2];
for (int i = 0; i < vornameArr.length; i++) {
vornameArr[i] = doubleBytes[i * 2]; // remove nulls
}
然后 运行 vornameArr
字节通过你的解码循环。 (如果愿意,您也可以在上述循环中应用解码转换。)
感谢@Gord Thompson 和他建议的网站 (fileformat.info) 我终于找到了答案:有时字符看起来很相似,并且出于某种原因在数据库中首选 "higher" (例如 unicode 字符 402 和 131)。我的 java 代码希望所有内容都具有较低的值,正如 excel 提供的那样。因此,如果代码高于 255,则需要将其替换为较低的值。出于某种原因,getBytes("cp1252")
将始终 return 较低的值,但是 toCharArray()
和 getBytes("UTF-16LE")
将 return 较高的正确值(比较:fileformat 192)
所以我的代码现在是这样的并且完美运行:
String vorname = row.getString("vorname");
char[] vornameArr = vorname.toCharArray();
for (int i = 0; i < vornameArr.length; i++)
{
if (vornameArr[i] > 255)
{
vornameArr[i] = (char) (String.valueOf(vornameArr[i]).getBytes("cp1252")[0] & 0xff);
}
vornameArr[i] = (char) (vornameArr[i] ^ DECRYPT_KEY[i]);
}
System.out.println(String.valueOf(vornameArr));
非常感谢您的帮助!