如何为 java 中的 UTF8 字符串做子串?

How to do substring for UTF8 string in java?

假设我有以下字符串:Rückruf ins Ausland 我需要将它插入到最大大小为 10 的数据库中。 我在 java 中做了一个普通的子字符串,它在 中提取了这个字符串 Rückruf,它有 10 个字符。当它尝试插入此列时,出现以下 oracle 错误:

java.sql.SQLException: ORA-12899: value too large for column "WAEL"."TESTTBL"."DESC" (actual: 11, maximum: 10) The reason for this is that the database has a AL32UTF8 character set thus the ü will take 2 chars.

我需要在 java 中编写一个函数来执行此子字符串,但考虑到 ü 占用 2 个字节,因此在这种情况下返回的子字符串应为 Rückruf i(9 个字符)。有什么建议吗?

我认为在这种情况下最好的选择是在数据库级别进行子字符串化,直接在 SQL QUERY 上使用 Oracle SUBSTR 函数。

例如:

INSERT INTO ttable (colname) VALUES (SUBSTR( ?, 1, 10 ))

其中感叹号代表通过JDBC发送的SQL参数。

您可以在 java 中计算 String 的正确长度,将字符串转换为字节数组。

例如,请参见下面的代码:

System.out.println("Rückruf i".length()); // prints 9 
System.out.println("Rückruf i".getBytes().length); // prints 10 

如果当前字符集不是 UTF-8,请将代码替换为:

System.out.println("Rückruf i".length()); // prints 9 
System.out.println("Rückruf i".getBytes("UTF-8").length); // prints 10 

如果需要,您可以将 UTF-8 替换为您想要测试该字符集中字符串长度的字符集。

您需要使数据库中的编码与 java 字符串的编码匹配。或者,您可以使用 this 之类的内容转换字符串并获得与数据库中的编码匹配的长度。这将为您提供准确的字节数。否则,您仍然只是希望编码匹配。

    String string = "Rückruf ins Ausland";

    int curByteCount = 0;
    String nextChar;
    for(int index = 0; curByteCount +  
         (nextChar = string.substr(index,index + 1)).getBytes("UTF-8").length < trimmedBytes.length;  index++){
        curByteCount += nextChar.getBytes("UTF-8").length;

    }
    byte[] subStringBytes = new byte[10];
    System.arraycopy(string.getBytes("UTF-8"), 0, subStringBytes, 0, curByteCount);
    String trimed = new String(subStringBytes, "UTF-8");

这应该可以做到。它还不应在此过程中截断多字节字符。这里假设数据库是UTF-8编码。另一个假设是字符串实际上需要修剪。

如果你想 trim Java 中的数据,你必须编写一个函数,该函数 trim 使用所用的 db 字符集对字符串进行 trim 处理,类似于这个测试用例:

package test;

import java.io.UnsupportedEncodingException;

public class TrimField {

    public static void main(String[] args) {
        //UTF-8 is the db charset
        System.out.println(trim("Rückruf ins Ausland",10,"UTF-8"));
        System.out.println(trim("Rüückruf ins Ausland",10,"UTF-8"));
    }

    public static String trim(String value, int numBytes, String charset) {
        do {
            byte[] valueInBytes = null;
            try {
                valueInBytes = value.getBytes(charset);
            } catch (UnsupportedEncodingException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
            if (valueInBytes.length > numBytes) {
                value = value.substring(0, value.length() - 1);
            } else {
                return value;
            }
        } while (value.length() > 0);
        return "";

    }

}

嘿,所有的 ASCII 字符都小于 128。你可以使用下面的代码。

public class Test {
    public static void main(String[] args) {
        String s= "Rückruf ins Ausland";
        int length =10;
        for(int i=0;i<s.length();i++){
            if(!(((int)s.charAt(i))<128)){
                length--;                   
            }
        }
        System.out.println(s.substring(0,length));
    }
}

您可以复制粘贴并检查它是否满足您的需要或它在任何地方中断。

如果它必须是 Java 你可以将字符串解析为字节和 trim 数组的长度。

        String s = "Rückruf ins Ausland";
        byte[] bytes = s.getBytes("UTF-8");
        byte[] bytes2 = new byte[10];
        System.arraycopy(bytes, 0, bytes2, 0, 10);
        String trim = new String(bytes2, "UTF-8");

以下可怕地通过完整的 Unicode 代码点遍历整个字符串,字符对(代理代码点)也是如此。

public String trim(String s, int length) {
    byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
    if (bytes.length <= length) {
        return s;
    }
    int totalByteCount = 0;
    for (int i = 0; i < s.length(); ) {
        int cp = s.codePointAt(i);
        int n = Character.charCount(cp);
        int byteCount = s.substring(i, i + n)
                .getBytes(StandardCharsets.UTF_8).length;
        if (totalByteCount + byteCount) > length) {
            break;
        }
        totalByteCount += byteCount;
        i += n;
    }
    return new String(bytes, 0, totalByteCount);
}

还可以再优化一下