【问题标题】:Can Java String.toUpperCase() ever fail?Java String.toUpperCase() 会失败吗?
【发布时间】:2016-02-25 09:12:05
【问题描述】:

情况:有一个 Java ESB,它从 Vaadin Web 表单中获取输入(姓氏),并应保证在将其保存到 DB 之前将其大写。

我被指派调查一个报告的问题,即 DB 中有时会出现小写字符。我了解到,该程序在通过 EntityManager 保存数据之前使用 String.toUpperCase() (它是唯一修改接收到的数据的地方)。

所以我想知道的是,这是否足够。到目前为止,我还没有发现任何与 toUpperCase() 函数相关的“众所周知的”问题,但我想确定一下。

所以问题是 - String.toUpperCase() 是否总能发挥作用?或者是否有任何可能出现错误且字母可能不是大写的字符或情况?

【问题讨论】:

  • 我会更关心你的代码在做什么,Java API...它已经测试了 20 多年了。
  • 如果你传递一个语言环境并且它是空的
  • 您可以在这里看到 Java 中的错误:search.oracle.com/search/…
  • 你有数据库中数据不是大写的例子吗?
  • 您可以先检查数据库中的小写字符。你有一个例子吗? (请提供字节码,不要输入字符)。

标签: java string uppercase


【解决方案1】:

Java String.toUpperCase() 会失败吗?

这取决于您是否传递了区域敏感字符串(见下文)。


Java.lang.String 的实现中,它只是使用默认语言环境:

public String toUpperCase() {
    return toUpperCase(Locale.getDefault());
}

toUpperCase(Locale) 使用给定区域设置的规则将此字符串中的所有字符转换为大写。大小写映射基于 Character 类指定的 Unicode 标准版本。由于大小写映射并不总是 1:1 字符映射,因此生成的字符串可能与原始字符串的长度不同

此方法区域设置敏感,如果用于旨在独立解释区域设置的字符串,可能会产生意外结果。示例包括编程语言标识符、协议密钥和 HTML 标记。

要获得不区分区域设置的字符串的正确结果,请使用 toUpperCase(Locale.ENGLISH)。

如果您对 toUpperCase(Locale) 的实现方式感兴趣:

public String toUpperCase(Locale locale) {
    if (locale == null) {
        throw new NullPointerException();
    }

    int firstLower;
    final int len = value.length;

    /* Now check if there are any characters that need to be changed. */
    scan: {
        for (firstLower = 0 ; firstLower < len; ) {
            int c = (int)value[firstLower];
            int srcCount;
            if ((c >= Character.MIN_HIGH_SURROGATE)
                    && (c <= Character.MAX_HIGH_SURROGATE)) {
                c = codePointAt(firstLower);
                srcCount = Character.charCount(c);
            } else {
                srcCount = 1;
            }
            int upperCaseChar = Character.toUpperCaseEx(c);
            if ((upperCaseChar == Character.ERROR)
                    || (c != upperCaseChar)) {
                break scan;
            }
            firstLower += srcCount;
        }
        return this;
    }

    /* result may grow, so i+resultOffset is the write location in result */
    int resultOffset = 0;
    char[] result = new char[len]; /* may grow */

    /* Just copy the first few upperCase characters. */
    System.arraycopy(value, 0, result, 0, firstLower);

    String lang = locale.getLanguage();
    boolean localeDependent =
            (lang == "tr" || lang == "az" || lang == "lt");
    char[] upperCharArray;
    int upperChar;
    int srcChar;
    int srcCount;
    for (int i = firstLower; i < len; i += srcCount) {
        srcChar = (int)value[i];
        if ((char)srcChar >= Character.MIN_HIGH_SURROGATE &&
            (char)srcChar <= Character.MAX_HIGH_SURROGATE) {
            srcChar = codePointAt(i);
            srcCount = Character.charCount(srcChar);
        } else {
            srcCount = 1;
        }
        if (localeDependent) {
            upperChar = ConditionalSpecialCasing.toUpperCaseEx(this, i, locale);
        } else {
            upperChar = Character.toUpperCaseEx(srcChar);
        }
        if ((upperChar == Character.ERROR)
                || (upperChar >= Character.MIN_SUPPLEMENTARY_CODE_POINT)) {
            if (upperChar == Character.ERROR) {
                if (localeDependent) {
                    upperCharArray =
                            ConditionalSpecialCasing.toUpperCaseCharArray(this, i, locale);
                } else {
                    upperCharArray = Character.toUpperCaseCharArray(srcChar);
                }
            } else if (srcCount == 2) {
                resultOffset += Character.toChars(upperChar, result, i + resultOffset) - srcCount;
                continue;
            } else {
                upperCharArray = Character.toChars(upperChar);
            }

            /* Grow result if needed */
            int mapLen = upperCharArray.length;
            if (mapLen > srcCount) {
                char[] result2 = new char[result.length + mapLen - srcCount];
                System.arraycopy(result, 0, result2, 0, i + resultOffset);
                result = result2;
            }
            for (int x = 0; x < mapLen; ++x) {
                result[i + resultOffset + x] = upperCharArray[x];
            }
            resultOffset += (mapLen - srcCount);
        } else {
            result[i + resultOffset] = (char)upperChar;
        }
    }
    return new String(result, 0, len + resultOffset);
}

【讨论】:

    【解决方案2】:

    如果没有任何进一步的信息,哪个字符(您决定为小写)存储在数据库中,我猜想起源类似于那些博客中解释的案例

    海因茨·卡布茨
    http://www.javaspecialists.eu/archive/Issue209.html
    http://www.javaspecialists.eu/archive/Issue211.html

    埃利奥特·鲁斯蒂·哈罗德
    http://cafe.elharo.com/blogroll/turkish/

    edit 可能是在数据库中存储了一个看起来与拉丁字符相似(基于字体)且不存在大写字母的字符。

    一个例子是GREEK LETTER YOT,它看起来类似于LATIN SMALL LETTER J,但没有大写字母。

    用于演示的小sn-p。

    int[] codePoints = { 0x03F3, 0x006A}; 
    for (int codePoint : codePoints) {
        char lowerCase = (char) Character.toLowerCase(codePoint);
        char upperCase = (char) Character.toUpperCase(codePoint);
        System.out.printf("Unicode name: %s%n", Character.getName(codePoint));
        System.out.printf("lowercase   : %s%n", lowerCase);
        System.out.printf("uppercase   : %s (%s)%n", upperCase,
            Character.isUpperCase(upperCase));
    }
    

    输出是

    Unicode name: GREEK LETTER YOT
    lowercase   : ϳ
    uppercase   : ϳ (false)
    Unicode name: LATIN SMALL LETTER J
    lowercase   : j
    uppercase   : J (true)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-02-04
      • 2019-03-27
      • 2016-08-20
      • 1970-01-01
      • 1970-01-01
      • 2023-03-24
      • 2011-05-07
      相关资源
      最近更新 更多