0

我有以下方法,它将字符串截断为特定大小的字节:

public class Utils {
    public static String trimStringToBytesSize(String s, int length) {
        if (s == null || length < 0) return null;
        int trimLength = Math.min(length, s.length());
        String trimmedString = s;
        while (trimmedString.getBytes().length > length && trimLength >= 0) {
            trimmedString = s.substring(0, trimLength);
            trimLength--;
        }
        return trimmedString;
    }
}

我为它写了一些测试:

@Test
public void trimStringToBytesSize() {
[...]
    trimStringToBytesSizeTestLogic("Шалом",
            6,
            "Шал"
    );
[...]
}

private void trimStringToBytesSizeTestLogic(final String input, final int
        stringLength, final String expectedResult) {
    final String actRes = Utils.trimStringToBytesSize(input, stringLength);
    Assert.assertEquals(expectedResult, actRes);
}

该测试在 IntelliJ Idea 中运行良好。但是,当我在 Gradle 中运行它时它失败了。错误是这样的:

org.junit.ComparisonFailure: expected:<Шал[]> but was:<Шал[ом]>

显然,它与字节大小有关。

我试图在一个包含方法和测试的最小项目中重现该问题。代码是一样的,但是这个最小的项目中没有出现在原始代码中出现的问题。

我试图找出它们之间的区别,并比较了最小和原始项目中的编码。根据 Notepad++ (UTF-8),它们是相同的。

还有什么可能导致此测试失败?我该如何解决?

注意:我使用的是 Java 1.8 和 Gradle 2.14(由于客户的要求,我无法升级到更新的版本)。

4

1 回答 1

2

你是对的,字符串的字节大小在很大程度上取决于你使用从字符串生成字节的编码。当您String.getBytes()不带参数使用时,将使用默认编码。这是UTF-8在 *nix 系统和 Windows 系统上的“ISO-8859-1”上。

Шалом您的UTF-8字节字符串是[-48, -88, -48, -80, -48, -69, -48, -66, -48, -68]. 你的字节
字符串是有效的,因为你的字符不能被编码。ШаломISO-8859-1[63, 63, 63, 63, 63]?????ISO-8859-1

因此,当您的测试成功时,您将拥有UTF-8编码,如果失败,您将拥有ISO-8859-1只有 5 个字节的编码,因此不会触及字符串。

您几乎不应该使用类似String getBytes()new String()不指定显式编码的方法,或者您在不同的操作系统或不同的上下文中总是有不同的行为。

于 2017-11-29T10:44:45.110 回答