考虑到字符编码,计算字符字节长度的最有效方法是什么?编码只在运行时才知道。例如,在UTF-8中,字符具有可变字节长度,因此需要单独确定每个字符。到目前为止,我已经想出了这个:
char c = getCharSomehow();
String encoding = getEncodingSomehow();
// ...
int length = new String(new char[] { c }).getBytes(encoding).length;
但是这在循环中是笨拙和低效的,因为每次都需要创建new String
。我在Java API中找不到其他更有效的方法。有String#valueOf(char)
,但根据其来源,它基本上与上面相同。我想这可以通过像位移这样的按位操作来完成,但这是我的弱点,我不确定如何在这里考虑编码:)
如果您对此有疑问,请检查this topic。
更新:来自@Bkkbrad的答案在技术上效率最高:
char c = getCharSomehow();
String encoding = getEncodingSomehow();
CharsetEncoder encoder = Charset.forName(encoding).newEncoder();
// ...
int length = encoder.encode(CharBuffer.wrap(new char[] { c })).limit();
然而正如@Stephen C指出的那样,这方面存在更多问题。例如,可能需要考虑组合/代理字符。但这是另一个需要在步骤之前解决的问题。
答案 0 :(得分:10)
使用CharsetEncoder并重复使用CharBuffer作为输入,并使用ByteBuffer作为输出。
在我的系统上,以下代码需要25秒来编码100,000个单个字符:
Charset utf8 = Charset.forName("UTF-8");
char[] array = new char[1];
for (int reps = 0; reps < 10000; reps++) {
for (array[0] = 0; array[0] < 10000; array[0]++) {
int len = new String(array).getBytes(utf8).length;
}
}
但是,以下代码在4秒内完成相同的操作:
Charset utf8 = Charset.forName("UTF-8");
CharsetEncoder encoder = utf8.newEncoder();
char[] array = new char[1];
CharBuffer input = CharBuffer.wrap(array);
ByteBuffer output = ByteBuffer.allocate(10);
for (int reps = 0; reps < 10000; reps++) {
for (array[0] = 0; array[0] < 10000; array[0]++) {
output.clear();
input.clear();
encoder.encode(input, output, false);
int len = output.position();
}
}
编辑:为什么仇敌会讨厌?
这是一个从CharBuffer读取并跟踪surrogate pairs的解决方案:
Charset utf8 = Charset.forName("UTF-8");
CharsetEncoder encoder = utf8.newEncoder();
CharBuffer input = //allocate in some way, or pass as parameter
ByteBuffer output = ByteBuffer.allocate(10);
int limit = input.limit();
while(input.position() < limit) {
output.clear();
input.mark();
input.limit(Math.max(input.position() + 2, input.capacity()));
if (Character.isHighSurrogate(input.get()) && !Character.isLowSurrogate(input.get())) {
//Malformed surrogate pair; do something!
}
input.limit(input.position());
input.reset();
encoder.encode(input, output, false);
int encodedLen = output.position();
}
答案 1 :(得分:3)
编码方案有可能将给定字符编码为可变字节数,具体取决于字符序列中前后的内容。因此,从编码单个字符串得到的字节长度不是完整的答案。
(例如,理论上你可以接收每3个字节编码为4个字符的baudot / teletype字符,或者你理论上可以将UTF-16 +流压缩器视为编码方案。是的,它有点难以置信,但是......)
答案 2 :(得分:3)
如果您可以保证输入格式良好的UTF-8,则根本没有理由找到代码点。 UTF-8的优势之一是您可以从字符串中的任何位置检测代码点的开始。只需向后搜索,直到找到一个字节(b&amp; 0xc0)!= 0x80,然后你就找到了另一个字符。由于UTF-8编码的代码点总是6个字节或更少,因此可以将中间字节复制到固定长度的缓冲区中。
编辑:我忘了提及,即使你没有采用这种策略,使用Java“char”存储任意代码点是不够的,因为代码点值可能超过0xffff。您需要将代码点存储在“int”中。
答案 3 :(得分:1)
尝试Charset.forName("UTF-8").encode("string").limit();
可能会更有效率,也许不会。