java中两个字符串的异或运算
Posted
技术标签:
【中文标题】java中两个字符串的异或运算【英文标题】:XOR operation with two strings in java 【发布时间】:2011-07-04 20:09:09 【问题描述】:如何在java中对两个字符串进行按位异或运算。
【问题讨论】:
您需要细化您的问题。你期待什么结果?可以举个例子吗? 我对您想要实现的目标感兴趣。也许某种加密? :) 是的。我想加密并获取另一个字符串。 你可以使用Java Cryptography API download.oracle.com/javase/1.5.0/docs/guide/security/jce/… 【参考方案1】:此解决方案与 android 兼容(我自己测试并使用过)。感谢@user467257,我从中改编了这个解决方案。
import android.util.Base64;
public class StringXORer
public String encode(String s, String key)
return new String(Base64.encode(xorWithKey(s.getBytes(), key.getBytes()), Base64.DEFAULT));
public String decode(String s, String key)
return new String(xorWithKey(base64Decode(s), key.getBytes()));
private byte[] xorWithKey(byte[] a, byte[] key)
byte[] out = new byte[a.length];
for (int i = 0; i < a.length; i++)
out[i] = (byte) (a[i] ^ key[i%key.length]);
return out;
private byte[] base64Decode(String s)
return Base64.decode(s,Base64.DEFAULT);
private String base64Encode(byte[] bytes)
return new String(Base64.encode(bytes,Base64.DEFAULT));
【讨论】:
谢谢!几点注意事项:base64Encode()
不在任何地方使用,最好使用Base64.NO_WRAP
进行编码以避免换行。【参考方案2】:
abs 函数是当字符串长度不同时,结果的长度将与两个字符串 a 和 b 的最小长度相同
public String xor(String a, String b)
StringBuilder sb = new StringBuilder();
for(int k=0; k < a.length(); k++)
sb.append((a.charAt(k) ^ b.charAt(k + (Math.abs(a.length() - b.length()))))) ;
return sb.toString();
【讨论】:
你真的不需要循环计算abs。【参考方案3】:注意:这仅适用于低字符,即低于 0x8000,这适用于所有 ASCII 字符。
我会对每个 charAt() 进行 XOR 以创建一个新字符串。喜欢
String s, key;
StringBuilder sb = new StringBuilder();
for(int i = 0; i < s.length(); i++)
sb.append((char)(s.charAt(i) ^ key.charAt(i % key.length())));
String result = sb.toString();
回应@user467257 的评论
如果您的输入/输出是 utf-8 并且您对“a”和“æ”进行异或运算,则会留下一个由一个字符(十进制 135,一个连续字符)组成的无效 utf-8 字符串。
正在异或的是 char
值,但是字节值会产生一个可以 UTF-8 编码的字符。
public static void main(String... args) throws UnsupportedEncodingException
char ch1 = 'a';
char ch2 = 'æ';
char ch3 = (char) (ch1 ^ ch2);
System.out.println((int) ch3 + " UTF-8 encoded is " + Arrays.toString(String.valueOf(ch3).getBytes("UTF-8")));
打印
135 UTF-8 encoded is [-62, -121]
【讨论】:
我检查i<s1.length() && i<s2.length()
,所以字符串不必是相同的长度。生成的字符串将是最短的长度。
首先,生成的字符串没有正确异或,因为您无法通过再次与密钥异或来恢复原始字符串(除非您的密钥保证等于或比非常奇怪的消息长)使代码完全歪曲了异或的概念。其次,不能保证通过简单的异或字符得到有效的字符串字节,所以你的输出字符串可能包含无效的字节序列。
@user467257 我认为您混淆了char
和byte
,它们不是一回事。我已通过回复您的评论更新了我的答案。
我删除了我的两个cmets,因为有太多不准确的地方。我认为额外字节的“插入”实际上发生在转换为 char 的点,因为 char 将指向具有两个字节 utf-8 表示的代码点)。我想我可以想出一个更好的例子来说明 char wise xoring 失败,周末再考虑。
@PeterLawrey 当您按照您的答案建议逐个字符异或时,只有限制。这是一个黑客解决方案,准备诱捕粗心的人。更好的方法是逐字节异或,base64(或其他)编码结果以确保可打印性/可读性,然后反转这些步骤进行解码。【参考方案4】:
这是我正在使用的代码:
private static byte[] xor(final byte[] input, final byte[] secret)
final byte[] output = new byte[input.length];
if (secret.length == 0)
throw new IllegalArgumentException("empty security key");
int spos = 0;
for (int pos = 0; pos < input.length; ++pos)
output[pos] = (byte) (input[pos] ^ secret[spos]);
++spos;
if (spos >= secret.length)
spos = 0;
return output;
【讨论】:
嗨,你能解释一下,这究竟应该如何工作。 您好,请您向我解释一下,这应该如何工作。我的想法是这样的:创建一个“秘密” 1.使用上面的代码创建编码字符串并将其添加到源中。 2. 在运行时解码这个编码字符串。每次我都会使用相同的秘密和相同的算法。我的问题是在哪里隐藏秘密,潜在的黑客将无法获得我的公钥【参考方案5】:你想要这样的东西:
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import java.io.IOException;
public class StringXORer
public String encode(String s, String key)
return base64Encode(xorWithKey(s.getBytes(), key.getBytes()));
public String decode(String s, String key)
return new String(xorWithKey(base64Decode(s), key.getBytes()));
private byte[] xorWithKey(byte[] a, byte[] key)
byte[] out = new byte[a.length];
for (int i = 0; i < a.length; i++)
out[i] = (byte) (a[i] ^ key[i%key.length]);
return out;
private byte[] base64Decode(String s)
try
BASE64Decoder d = new BASE64Decoder();
return d.decodeBuffer(s);
catch (IOException e) throw new RuntimeException(e);
private String base64Encode(byte[] bytes)
BASE64Encoder enc = new BASE64Encoder();
return enc.encode(bytes).replaceAll("\\s", "");
base64 编码已完成,因为对字符串的字节进行异或可能无法返回字符串的有效字节。
【讨论】:
很好的答案!但读者应确保使用java.util.Base64
而不是soon-to-be-unreachable classes from sun.misc
。
我将此示例与 android.Base64 一起使用,而不是 sun:import android.util.Base64;这两个方法也改为: private byte[] base64Decode(String s) try return Base64.decode(s,Base64.DEFAULT); catch (IllegalArgumentException e) throw new RuntimeException(e); private String base64Encode(byte[] bytes) return Base64.encodeToString(bytes,Base64.DEFAULT).replaceAll("\\s", ""); 【参考方案6】:
注意:
Java char
对应一个 UTF-16 代码单元,在某些情况下,一个真正的 Unicode 字符(代码点)。
异或两个有效的 UTF-16 序列(即 Java 字符串 char
与 char
,或编码为 UTF-16 后逐字节)并不一定会给您另一个有效的 UTF-16 字符串 - 您可能有未配对的代理项因此。 (它仍然是一个完全可用的 Java 字符串,只是与代码点有关的方法可能会混淆,以及转换为其他编码以进行输出和类似的方法。)
如果您首先将字符串转换为 UTF-8,然后对这些字节进行异或运算,则同样有效 - 在这里,您 很可能 将得到一个不是有效 UTF-8 的字节序列,如果您的字符串还不是纯 ASCII 字符串。
即使您尝试正确执行并按代码点迭代两个字符串并尝试对代码点进行异或,您最终可能会得到超出有效范围的代码点(例如,U+FFFFF
(平面 15)XOR U+10000
(平面 16)= U+1FFFFF
(这将是平面 31 的最后一个字符),远高于现有代码点的范围。您也可以以这种方式结束为代理保留的代码点(= 无效的代码点)。
如果您的字符串仅包含
我最后要说的是:不要期望加密字符串的结果再次成为有效字符串 - 相反,只需将其存储为 byte[]
(或流字节)。 (是的,加密前转换为 UTF-8,解密后转换为 UTF-8)。
【讨论】:
Java 在内部使用什么是无关紧要的。作为用户,您可以访问每个 char(当然还有代理问题)或每个代码点。 Java 是否在内部使用 UTF-16 或小恐惧所穿的moonboots 的颜色与这个问题无关。 @SyntaxT3rr0r:好的,也许措辞不够理想,我正在尝试编辑。 @SyntaxT3rr0r:按代码点进行异或也无济于事(请参阅答案中的示例)。 +1 - 我同意保罗的观点。 XOR-ing 有责任破坏使 Java 字符串成为有效 UTF-16 字符串的属性。如果你这样做,它们就无法编码/解码。【参考方案7】:假设 (!) 字符串长度相等,为什么不 convert the strings to byte arrays 然后 XOR 字节。根据您的编码,生成的字节数组也可能具有不同的长度(例如,UTF8 将针对不同的字符扩展为不同的字节长度)。
您应该小心指定字符编码以确保一致/可靠的字符串/字节转换。
【讨论】:
字符串的长度可能相同,但字节数组的长度可能不同。 ;) @PeterLawrey 你能解释一下字节数组的长度何时可以不同吗? 如果您有"$".getBytes()
,它可能是 1 个字节,“£”可能是 2 个字节,“€”可能是 3 个字节。 (它们是 UTF-8)
@PeterLawrey 谢谢!祝你有美好的一天!
澄清一下,Java 中的代码点可以介于 0 (Character.MIN_CODE_POINT) 和 0x10FFFF (Character.MAX_CODE_POINT) 之间以上是关于java中两个字符串的异或运算的主要内容,如果未能解决你的问题,请参考以下文章
LeetCode 1442 形成两个异或相等数组的三元组数目[异或 位运算 数学] HERODING的LeetCode之路