我正在构建一个小型应用程序,将文本文件中的文本转换为 Base64,然后恢复正常。解码后的文本总是在第一行的开头返回一些汉字。
public EncryptionEngine(File appFile){
this.appFile= appFile;
}
public void encrypt(){
try {
byte[] fileText = Files.readAllBytes(appFile.toPath());// get file text as bytes
Base64.Encoder encoder = Base64.getEncoder();
PrintWriter writer = new PrintWriter(appFile);
writer.print("");//erase old, readable text
writer.print(encoder.encodeToString(fileText));// insert encoded text
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void deycrpt(){
try {
byte[] fileText = Files.readAllBytes(appFile.toPath());
String s = new String (fileText, StandardCharsets.UTF_8);//String s = new String (fileText);
Base64.Decoder decoder = Base64.getDecoder();
byte[] decodedByteArray = decoder.decode(s);
PrintWriter writer = new PrintWriter(appFile);
writer.print("");
writer.print(new String (decodedByteArray,StandardCharsets.UTF_8)); //writer.print(new String (decodedByteArray));
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
加密之前的文本文件():
奶酪
西红柿
土豆
火腿
山药
加密后的文本文件() //5jAGgAZQBlAHMAZQANAAoAdABvAG0AYQB0AG8AZQBzAA0ACgBwAG8AdABhAHQAbwBlAHMADQAKAGgAYQBtAHMADQAKAHkaYQBtAHMA
解密后的文本文件
뿯붿奶酪
西红柿
土豆
火腿
山药
您的输入文件是 UTF-16,而不是 UTF-8。它以
FF FE
开头,即小端字节顺序标记。 StandardCharsets.UTF_16
将正确处理这个问题。 (或者,将文本编辑器设置为 UTF-8 而不是 UTF-16。)
当您将
fffe
解码为 UTF-8 时,您会得到两个替换字符 "��"
,每个字符对应 UTF-8 中无效的两个字节。然后,当您打印出来时,每个替换字符 '�'
都被编码为 UTF-8 中的 ef bf bd
。然后,您将结果解释为 UTF-16,将它们分成两个组,将其读作 efbf bdef bfbd
。文件的其余部分始终是 UTF-16,但空字节将安全地往返。
(如果文件是 UTF-16 的 ascii 文本编码,没有字节顺序标记,你不会注意到这是多么糟糕!)
您的加密和解密函数不会做出相同的假设。 encrypt Base64 对任何文件进行编码,除了表明该文件是文本文件的变量名称和注释之外,都很好。不一定是这样。
decrypt 将 Base64 编码的数据反转回字节,但随后通过假设字节是使用 UTF-8 进行文本编码、然后解码并在将它们写入文件之前重新编码来“过度处理”。如果假设成立,那么它只是一个 NOP;对于您的情况来说,这显然是不正确的,并且它会破坏数据。
也许您这样做是因为您尝试使用 PrintWriter。在 Java(和 .NET)中,多个流和文件 I/O 类常常令人困惑,特别是考虑到它们长达数十年的演变。有时,有一个产品可以满足您的需求,但可能很难找到;其他时候则没有。有时,像 Apache Commons 这样的常用库可以填补这个空白。
因此,只需将字节写入文件即可。正如这个直接问题byte[] to file in Java 的答案中所解释的,有许多现代和历史选项。这是一个带有 Files.write:
Files.write(appFile.toPath(), decodedByteArray, StandardOpenOption.CREATE);
注意:虽然 Base64 在几百年前可能被认为是加密(并被破解),但它并不是用于此目的。这样称呼它有点危险(并且令人困惑)。