`
RednaxelaFX
  • 浏览: 3019438 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

String#unpack对应的UTF-8是怎么回事?

    博客分类:
  • Ruby
阅读更多
Ruby每周一测 - 中英文混合字符串截取

Quake Wang发的这个测试相当有趣,值得一看。我也算是被Ruby的字符编码问题困扰了好段时间了,这次果然又中招了。
老庄的解法:
庄表伟 写道
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


看到老庄的解法,我的第一直觉是:UTF-8中CJK应该是三字节的啊,这样unpack之后算出来的值不是不对了么?然后看看RDoc怎么说的:
-------+---------+-----------------------------------------
  U    | Integer | UTF-8 characters as unsigned integers
-------+---------+-----------------------------------------

看到这个文档我还以为是把UTF-8的字符串拆成字节,结果原来是每个字符对应一个整型数字。

可是这个对应关系到底是怎样的……String#unpack的实现在pack.c里:
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;


然后单个字符的转换函数是:
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的说明再看看……
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics