如何在java中为UTF8字符串创建子字符串?

如何在java中为UTF8字符串创建子字符串?,java,oracle,substring,Java,Oracle,Substring,假设我有以下字符串:Rückruf ins Ausland我需要将其插入最大大小为10的数据库中。 我用java做了一个普通的子字符串,它在中提取了这个字符串Rückruf,共10个字符。当它尝试插入此列时,我收到以下oracle错误: java.sql.SQLException:ORA-12899:列的值太大 “WAEL”“TESTTBL”“DESC”(实际值:11,最大值:10) 原因是数据库有一个AL32UTF8字符集,因此ü将占用2个字符 我需要用java编写一个函数来处理这个子字符串,

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

java.sql.SQLException:ORA-12899:列的值太大 “WAEL”“TESTTBL”“DESC”(实际值:11,最大值:10) 原因是数据库有一个AL32UTF8字符集,因此ü将占用2个字符


我需要用java编写一个函数来处理这个子字符串,但是考虑到ü需要2个字节,所以在这种情况下返回的子字符串应该是Rückruf I(9个字符)。有什么建议吗?

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

例如:

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

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

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

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

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字符串的编码匹配。或者,您可以使用类似的方法转换字符串,并获得与数据库中的编码匹配的长度。这将为您提供准确的字节计数。否则,您仍然希望编码匹配

    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");
String=“Rückruf ins Ausland”;
int-corbytecount=0;
字符串nextChar;
对于(int index=0;curByteCount+
(nextChar=string.substr(index,index+1)).getBytes(“UTF-8”).length

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

如果您想修剪Java中的数据,您必须编写一个函数,使用所使用的db字符集修剪字符串,类似于以下测试用例:

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));
    }
}
公共类测试{
公共静态void main(字符串[]args){
字符串s=“Rückruf ins Ausland”;
整数长度=10;

对于(inti=0;i如果必须是Java,则可以将字符串解析为字节并修剪数组的长度

        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代码点,以及字符对(代理代码点),间接地遍历整个字符串

公共字符串修剪(字符串s,整数长度){
byte[]bytes=s.getBytes(StandardCharsets.UTF_8);
如果(字节数.长度){
打破
}
totalByteCount+=字节计数;
i+=n;
}
返回新字符串(字节,0,totalByteCount);
}

它仍然可以进行一些优化。

是的,但是如果基础数据库字符集将更改为其他字符集呢?字节数可能会根据所使用的编码而变化。因此这不是通用的。这是java中使用的字节数。如果UTF8中存在字符,则表示为1个字节,如果它是UTF16而不是p字符REENT在UTF8中由2个字节表示。我明白你的意思,它在Java中是一致的,但我的意思是,它不一定总是与数据库字节匹配。这也取决于数据库中的编码。在这种特定情况下,它是匹配的。@Singh也许你的默认字符集不是UTF-8?如果是,你必须用System替换。out.println(“Rückruf i”.getBytes(“UTF-8”).length);我添加了非UTF-8字符集的解决方案不应该
length=9;
be
length--;
并且没有中断?如果有两个
“ü”
在字符串中?是的,对..我的错..让我编辑它,我也打破了循环。此解决方案仅适用于10个字符的字符串,最多一个“两个字节”char。任何其他字符串总是返回9或10或抛出一个索引xOfFangBysExchange。是的,我根据上面的注释改变了解决方案。现在可以检查它吗?现在它正在动态地为任何解决方案工作。如果你在截断之前有一个2字节的字符开始,不会在一个2字节字符的中间截断,然后存储DA。ta不正确?或者在这种情况下会发生什么?这很有效,而且它的优点是没有任何循环。它是直接向前的。我很确定如果多字节字符位于修剪边界,它将截断这些字符。我的解决方案基于此,但我循环并检查新字符不会交叉边界。是的,你是对的,我尝试了一个像123456789ü这样的例子,修剪后的字符串是123456789?,在end@CarlosBribiescas你说得对,我没想过!也许可以选择使用字符长度语义来定义列长度。
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);
}