由一道题引出的一些东西(求助文章!)
题目: 用io对进行文件简单的加密解密
思路一: 用字节流读取出来存入byte数组,然后创建String对象,再追加到StringBuilder对象上. 然后一直循环追加,最后再将StringBuilder对象转为String对象,再转回byte数组,最后存入文件中.
代码演示:
//加密代码
public static void main(String[] args) throws IOException {
byStringBuilder();
}
public static void byStringBuilder() throws IOException {
//读取图片
BufferedInputStream inBuffer =
new BufferedInputStream(new FileInputStream("src/lab/second/page_643/in.jpg"), 1024);
//存读取的字节
byte[] storageArr = new byte[1024];
//用StringBuilder将byte数组先拼接,然后转回为byte数组
StringBuilder builder = new StringBuilder();
int read;
while ((read = inBuffer.read(storageArr, 0, storageArr.length)) != -1) {
//先根据byte数组创建String对象,然后追加到StringBuilder对象上
builder.append(new String(storageArr, 0, read));
}
inBuffer.close();
//重新转化为byte
byte[] bytes = new String(builder).getBytes();
//加密方式为byte数组每个元素加5
for (int i = 0; i < bytes.length; i++)
bytes[i] += 5;
//存入out.jpg文件中
BufferedOutputStream outBuffer = new BufferedOutputStream(
new FileOutputStream("src/lab/second/page_643/out.jpg"), 1024);
outBuffer.write(bytes);
outBuffer.close();
}
//解密代码
public static void main(String[] args) throws IOException {
byStringBuilder();
}
public static void byStringBuilder() throws IOException {
String path = "src/lab/second/page_643/out.jpg";
//读取加密的文件
BufferedInputStream inBuffer =
new BufferedInputStream(new FileInputStream(path));
BufferedInputStream buffer = new BufferedInputStream(inBuffer, 1024);
//用byte数组存读取的字节
byte[] storage = new byte[1024];
int read;
//读出来的字节,用StringBuilder拼接
StringBuilder builder = new StringBuilder();
while ((read = buffer.read(storage, 0, storage.length)) != -1) {
//追加到builder对象上
builder.append(new String(storage, 0, read));
}
//转为byte数组
byte[] bytes = builder.toString().getBytes();
//解密
for (int i = 0; i < bytes.length; i++)
bytes[i] -= 5;
//写到同一个文件中,将原来加密的文件覆盖
BufferedOutputStream outBuffer = new BufferedOutputStream(
new FileOutputStream("src/lab/second/page_643/out.jpg"), 1024);
outBuffer.write(bytes);
outBuffer.close();
}
思路二: 不用StringBuilder,读出来的字节放在storage这个byte数组中,然后复制一个byte数组,再将复制的byte数组放入ArrayList中,最后向文件循环写出list中的byte数组.
代码演示:
//加密代码
public static void main(String[] args) throws IOException {
byArrayList();
}
public static void byArrayList() throws IOException {
//读取图片
BufferedInputStream inBuffer = new BufferedInputStream(
new FileInputStream("src/lab/second/page_643/in.jpg"), 1024);
//存读取的字节的byte数组
byte[] storageArr = new byte[1024];
//存若干个byte数组(因为图片大小肯定大于1kb,所以用若干数组存)
ArrayList<byte[]> list = new ArrayList<>();
int read;
while ((read = inBuffer.read(storageArr, 0, storageArr.length)) != -1) {
//拷贝数组
byte[] clone = Arrays.copyOfRange(storageArr, 0, read);
//拷贝后添加到list中
list.add(clone);
}
inBuffer.close();
//所有元素值加5
for (byte[] bytes : list) {
addFive(bytes);
}
//存入out.jpg文件中
BufferedOutputStream outStream = new BufferedOutputStream(
new FileOutputStream("src/lab/second/page_643/out.jpg"), 1024);
for (byte[] bytes : list) {
outStream.write(bytes);
}
outStream.close();
}
//给byte数组,每个元素的值加5
public static void addFive(byte[] arr) {
for (int i = 0; i < arr.length; i++)
arr[i] += 5;
}
//解密代码
public static void main(String[] args) throws IOException {
byArrayList();
}
public static void byArrayList() throws IOException {
//读取图片
BufferedInputStream inBuffer =
new BufferedInputStream(new FileInputStream("src/lab/second/page_643/out.jpg"), 1024);
//存读取的字节
byte[] storageArr = new byte[1024];
//存复制的byte数组
ArrayList<byte[]> list = new ArrayList<>();
int read;
while ((read = inBuffer.read(storageArr, 0, 1024)) != -1) {
//将byte数组克隆,并将其加到list中
byte[] clone = Arrays.copyOfRange(storageArr, 0, read);
list.add(clone);
}
for (byte[] bytes : list) {
divide(bytes);
}
//没开追加模式,直接将原来加密文件覆盖
BufferedOutputStream outBuffer = new BufferedOutputStream(
new FileOutputStream("src/lab/second/page_643/out.jpg"), 1024);
for (byte[] bytes : list) {
outBuffer.write(bytes);
}
outBuffer.close();
}
//给byte数组,每个元素值减5
public static void divide(byte[] arr) {
for (int i = 0; i < arr.length; i++)
arr[i] -= 5;
}
这种方式成功的实现了加密和解密,所以推断第一种方式问题出在字符串问题上. 询问GPT得知,可能是new String时未指定编码,然后果断在while循环中new String和获取字符串的byte数组时指定了utf8编码
builder.append(new String(storageArr, 0,read,StandardCharsets.UTF_8));
byte[] bytes = builder.toString().getBytes(StandardCharsets.UTF_8);
结果仍然不行,突然想起java中char和String好像是utf-16编码,所以果断去测试一把应该用哪种编码
根据最初的byte数组中的值,和经过不同编码方式转化为字符串再转换回来的值进行比较
表达得不是很清楚,直接上代码:
public static void main(String[] args) throws IOException {
//读取图片
BufferedInputStream inBuffer =
new BufferedInputStream(new FileInputStream("src/lab/second/page_643/in.jpg"), 1024);
//byte数组,存储读到的字节
byte[] bytes = new byte[1024];
inBuffer.read(bytes, 0, bytes.length);
//打印原本的
System.out.println("原本的:");
printArr(bytes);
//使用utf8编码转换为字符串,然后转换回来
StringBuilder builderUTF8 = new StringBuilder().append(new String(bytes, StandardCharsets.UTF_8));
byte[] arrUTF8 = builderUTF8.toString().getBytes(StandardCharsets.UTF_8);
System.out.println("utf8编码的:");
printArr(arrUTF8);
//使用utf16转换为字符串,然后转换回来
StringBuilder builderUTF_16 = new StringBuilder().append(new String(bytes, StandardCharsets.UTF_16));
byte[] arrUTF_16 = builderUTF_16.toString().getBytes(StandardCharsets.UTF_16);
System.out.println("utf16编码的:");
printArr(arrUTF_16);
//使用UTF_16BE转换为字符串,然后转换回来
StringBuilder builderUTF_16BE = new StringBuilder().append(new String(bytes, StandardCharsets.UTF_16BE));
byte[] arrUTF_16BE = builderUTF_16BE.toString().getBytes(StandardCharsets.UTF_16BE);
System.out.println("UTF_16BE编码的:");
printArr(arrUTF_16BE);
//使用UTF_16LE转换为字符串,然后转换回来
StringBuilder builderUTF_16LE = new StringBuilder().append(new String(bytes, StandardCharsets.UTF_16LE));
byte[] arrUTF_16LE = builderUTF_16LE.toString().getBytes(StandardCharsets.UTF_16LE);
System.out.println("UTF_16LE编码的:");
printArr(arrUTF_16LE);
}
//打印byte数组中的值
public static void printArr(byte[] bytes) {
for (int i = 0; i < 10; i++) {
System.out.print(bytes[i] + " ");
}
System.out.println("\n-------------");
}
运行结果:
可见,用UTF_16BE编码先转为字符串再转回byte数组,值是没有变的.
GPT似乎也肯定了我的想法,
改完后自信运行,仍然失败(哭
直接气急败坏把StandardCharsets类里面全部编码试一遍
仍然失败(哭
求大佬解答!
其他:
关于UTF_16BE和UTF_16LE:
个人理解: UTF_16BE就是从右往左存,UTF_16LE就是相反
官方话术:
GPT的回答:
在<<JAVA核心技术:卷二>>中也有一小段介绍:
研究这些,一起面试造火箭!(doge