[url=http://www.iteye.com/topic/201531]Ruby每周一测 - 中英文混合字符串截取[/url]
Quake Wang发的这个测试相当有趣,值得一看。我也算是被Ruby的字符编码问题困扰了好段时间了,这次果然又中招了。
老庄的解法:
[quote="庄表伟"]
看到老庄的解法,我的第一直觉是:UTF-8中CJK应该是三字节的啊,这样unpack之后算出来的值不是不对了么?然后看看RDoc怎么说的:
[code]-------+---------+-----------------------------------------
U | Integer | UTF-8 characters as unsigned integers
-------+---------+-----------------------------------------[/code]
看到这个文档我还以为是把UTF-8的字符串拆成字节,结果原来是每个字符对应一个整型数字。
可是这个对应关系到底是怎样的……String#unpack的实现在pack.c里:
[code]case 'U':
if (len > send - s) len = send - s;
while (len > 0 && s < send) {
long alen = send - s;
unsigned long l;
l = utf8_to_uv(s, &alen);
s += alen; len--;
rb_ary_push(ary, ULONG2NUM(l));
}
break;[/code]
然后单个字符的转换函数是:
先确定UTF-8字符的长度(字节数),然后在while循环里编码……但是那几个magic number到底是什么意思我还是没弄明白,主要是那个0x3f和6。回去翻翻UTF-8的说明再看看……
Quake Wang发的这个测试相当有趣,值得一看。我也算是被Ruby的字符编码问题困扰了好段时间了,这次果然又中招了。
老庄的解法:
[quote="庄表伟"]
def truncate_u(text, length = 30, truncate_string = "...")
l=0
char_array=text.unpack("U*")
char_array.each_with_index do |c,i|
l = l+ (c<127 ? 0.5 : 1)
if l>=length
return char_array[0..i].pack("U*")+(i<char_array.length-1 ? truncate_string : "")
end
end
return text
end
[/quote]
看到老庄的解法,我的第一直觉是:UTF-8中CJK应该是三字节的啊,这样unpack之后算出来的值不是不对了么?然后看看RDoc怎么说的:
[code]-------+---------+-----------------------------------------
U | Integer | UTF-8 characters as unsigned integers
-------+---------+-----------------------------------------[/code]
看到这个文档我还以为是把UTF-8的字符串拆成字节,结果原来是每个字符对应一个整型数字。
可是这个对应关系到底是怎样的……String#unpack的实现在pack.c里:
[code]case 'U':
if (len > send - s) len = send - s;
while (len > 0 && s < send) {
long alen = send - s;
unsigned long l;
l = utf8_to_uv(s, &alen);
s += alen; len--;
rb_ary_push(ary, ULONG2NUM(l));
}
break;[/code]
然后单个字符的转换函数是:
static unsigned long
utf8_to_uv(p, lenp)
char *p;
long *lenp;
{
int c = *p++ & 0xff;
unsigned long uv = c;
long n;
if (!(uv & 0x80)) {
*lenp = 1;
return uv;
}
if (!(uv & 0x40)) {
*lenp = 1;
rb_raise(rb_eArgError, "malformed UTF-8 character");
}
if (!(uv & 0x20)) { n = 2; uv &= 0x1f; }
else if (!(uv & 0x10)) { n = 3; uv &= 0x0f; }
else if (!(uv & 0x08)) { n = 4; uv &= 0x07; }
else if (!(uv & 0x04)) { n = 5; uv &= 0x03; }
else if (!(uv & 0x02)) { n = 6; uv &= 0x01; }
else {
*lenp = 1;
rb_raise(rb_eArgError, "malformed UTF-8 character");
}
if (n > *lenp) {
rb_raise(rb_eArgError, "malformed UTF-8 character (expected %d bytes, given %d bytes)",
n, *lenp);
}
*lenp = n--;
if (n != 0) {
while (n--) {
c = *p++ & 0xff;
if ((c & 0xc0) != 0x80) {
*lenp -= n + 1;
rb_raise(rb_eArgError, "malformed UTF-8 character");
}
else {
c &= 0x3f;
uv = uv << 6 | c;
}
}
}
n = *lenp - 1;
if (uv < utf8_limits[n]) {
rb_raiserb_eArgError, "redundant UTF-8 sequence");
}
return uv;
}
先确定UTF-8字符的长度(字节数),然后在while循环里编码……但是那几个magic number到底是什么意思我还是没弄明白,主要是那个0x3f和6。回去翻翻UTF-8的说明再看看……