使用不同编码编码的两个不同字符串可以具有相同的字节序列吗?

Posted

技术标签:

【中文标题】使用不同编码编码的两个不同字符串可以具有相同的字节序列吗?【英文标题】:Can two different strings when encoded with different encodings have the same byte sequence? 【发布时间】:2012-07-19 06:35:31 【问题描述】:

用不同编码编码的两个不同字符串可以具有相同的字节序列吗? 即以下示例中的一些“字符串一”和“字符串二”,当使用两种不同的编码进行编码时 (cp1252和utf-8只是例子)会导致测试通过吗?

import java.io.UnsupportedEncodingException;
import java.util.Arrays;

import org.junit.Assert;
import org.junit.Test;

public class EncodingTest 
    @Test
    public void test() throws UnsupportedEncodingException 
        final byte[] sequence1 = "string one".getBytes("Cp1252");
        final byte[] sequence2 = "string two".getBytes("UTF-8");
        Assert.assertTrue(Arrays.equals(sequence1, sequence2));
    

我的代码中的一个错误对使用 JVM 的默认编码的字符串生成的字节序列进行哈希处理,我需要验证当代码使用不同的字符串和不同的 JVM 文件编码运行时是否会导致哈希冲突(在运行时可能会发生这种情况)例如 Windows 和 Linux)。

由于编码是字节序列和字符之间的映射,我认为可能有一些字符串和编码通过了上述测试。但只是想知道是否有任何众所周知的例子或一些很好的理由说明为什么我不应该依赖哈希冲突不会发生。

谢谢

PS:这仅适用于 JDK 1.6 支持的编码,而不是某些虚构的编码。

【问题讨论】:

使用“默认编码”通常是..可疑的。 请注意,这个问题是在询问某些答案所响应的内容的inverse;不是两个相同具有不同编码的字符串是否可以生成相同的字节序列,而是询问两个不同字符串是否具有不同的编码可以生成相同的字节序列。 (更具体地说,如果存在这种碰撞的“已知案例”。) 为什么哈希冲突很重要?哈希码不需要是唯一的。 您确实意识到,除非您生活在一个最多有 40 亿个可能字符串的世界中,否则根据定义,就会出现哈希冲突。 【参考方案1】:

是的。举个简单的例子,编码为 ISO-8859-1 的字符串“¡”(倒置感叹号)和编码为 ISO-8859-2 的字符串“Ą”(大写 A 和 ogoned)都成为单字节序列A1(十六进制)。当使用将字符映射到单个字节的非常简单的编码时,或多或少会发生这种情况。否则它们不会是不同的编码。当涉及更复杂的编码方案时,肯定会发生这种情况。

【讨论】:

【参考方案2】:

这是一个简单的方法:大多数代码页和 UTF-8 共享 ASCII 编码 (0x00 = 0x7F)。如果您的文本是简单的英文,那么它很有可能是 ASCII——无论声明的编码是什么,因为它主要使用简单的非重音字符。

【讨论】:

这如何导致两个 不同 输入字符串以 getBytes() 中的相同字节结尾?【参考方案3】:

如果源字符串的编码支持多字节字符,而目标编码不支持多字节字符,那么发生冲突似乎是合理的,因为多字节字符需要映射到单字节字符集。

例如如果输入的字符串是用中文写的,目标字符集是US-ASCII,那么很多中文字符肯定会映射到相同的US-ASCII表示。

【讨论】:

【参考方案4】:

这段代码最终应该会产生一个例子:

    while(true)
        Random r = new Random();
        byte[] bytes = new byte[4];
        r.nextBytes(bytes);
        try
            String raw = Arrays.toString(bytes);
            String utf8 = new String(bytes, "UTF-8");
            String latin1 = new String(bytes, "ISO-LATIN-1");
            System.out.println(raw + " is " + utf8 + " or " + latin1);
            break;
        catch(Exception e)
    

【讨论】:

【参考方案5】:

是的,至少对于不同长度的字符串是可能的。

字符串"\u2020"(或"†")在UTF-16 中编码为0x20,0x20。这也是"\x20\x20"(由两个 ASCII 空格组成的字符串)在 ASCII 中编码的内容。

当然,The Dagger 在语言中并不经常出现 [=^_^=],但一些标准的 [非拉丁语] 字母可以生成映射到标准(非拉丁语)的类似字节序列控制字符)ASCII编码..如果控制字符的限制放宽,还有更多。

如果发现两个相似的“现实”字符串(例如相同长度和“敏感数据”)可以映射到具有不同编码的相同字节序列的情况会更有趣..

【讨论】:

以上是关于使用不同编码编码的两个不同字符串可以具有相同的字节序列吗?的主要内容,如果未能解决你的问题,请参考以下文章

不同编码方式1个英文字母占的字节是不同的

字符编码相关

几种不同的字符编码方式

不同编码所占字节

编码格式

比较使用不同编码存储的相同值