【问题标题】:How to get code point number for a given character in a utf-8 string?如何获取 utf-8 字符串中给定字符的代码点编号?
【发布时间】:2010-09-28 14:12:31
【问题描述】:

我想获取给定 UTF-8 字符串的 UCS-2 代码点。例如,单词“hello”应该变成“0068 0065 006C 006C 006F”。请注意,字符可能来自任何语言,包括东亚语言等复杂的脚本。

因此,问题归结为“将给定字符转换为其 UCS-2 代码点”

但是怎么做呢?拜托,任何形式的帮助都将非常感谢,因为我很着急。


将提问者的回答转录为答案

感谢您的回复,但需要在 PHP v 4 或 5 而不是 6 中完成。

字符串将是来自表单域的用户输入。

我想实现一个 utf8to16 或 utf8decode 之类的 PHP 版本

function get_ucs2_codepoint($char)
{
    // calculation of ucs2 codepoint value and assign it to $hex_codepoint
    return $hex_codepoint;
}

你能帮我用 PHP 还是用上面提到的版本的 PHP 来做?

【问题讨论】:

    标签: php unicode


    【解决方案1】:

    使用现有的实用程序,例如 iconv,或您使用的语言附带的任何库。

    如果您坚持推出自己的解决方案,请阅读UTF-8 格式。基本上,每个代码点存储为 1-4 个字节,具体取决于代码点的值。范围如下:

    • U+0000 — U+007F:1字节:0xxxxxxx
    • U+0080 — U+07FF:2字节:110xxxxxx 10xxxxxx
    • U+0800 — U+FFFF:3字节:1110xxxx 10xxxxxx 10xxxxxx
    • U+10000 — U+10FFFF:4字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

    其中每个 x 是一个数据位。因此,您可以通过查看第一个字节来判断每个代码点由多少字节组成:如果它以 0 开头,则它是一个 1 字节字符。如果它以 110 开头,则它是一个 2 字节字符。如果它以 1110 开头,则它是一个 3 字节字符。如果以 11110 开头,则为 4 字节字符。如果它以 10 开头,则它是多字节字符的非初始字节。如果以 11111 开头,则为无效字符。

    一旦你弄清楚字符中有多少字节,这只是一个问题。另请注意,UCS-2 不能表示 U+FFFF 以上的字符。

    由于您没有指定语言,这里有一些示例 C 代码(省略了错误检查):

    wchar_t utf8_char_to_ucs2(const unsigned char *utf8)
    {
      if(!(utf8[0] & 0x80))      // 0xxxxxxx
        return (wchar_t)utf8[0];
      else if((utf8[0] & 0xE0) == 0xC0)  // 110xxxxx
        return (wchar_t)(((utf8[0] & 0x1F) << 6) | (utf8[1] & 0x3F));
      else if((utf8[0] & 0xF0) == 0xE0)  // 1110xxxx
        return (wchar_t)(((utf8[0] & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6) | (utf8[2] & 0x3F));
      else
        return ERROR;  // uh-oh, UCS-2 can't handle code points this high
    }
    

    【讨论】:

      【解决方案2】:

      我很高兴,因为我刚刚在期末考试中把这个问题交给了学生。这是 UTF-8 的草图:

      hex         binary                   UTF-8 binary
      0000-007F   00000000 0abcdefg   =>   0abcdefg
      0080-07FF   00000abc defghijk   =>   110abcde 10fghijk
      0800-FFFF   abcdefgh ijklmnop   =>   1110abcd 10efghij 10klmnop
      

      这里有一些 C99 代码:

      static void check(char c) {
        if ((c & 0xc0) != 0xc0) RAISE(Bad_UTF8);
      }
      
      uint16_t Utf8_decode(char **p) { // return code point and advance *p
        char *s = *p;
        if ((s[0] & 0x80) == 0) {
          (*p)++;
          return s[0];
        } else if ((s[0] & 0x40) == 0) {
          RAISE (Bad_UTF8);
          return ~0; // prevent compiler warning
        } else if ((s[0] & 0x20) == 0) {
          if ((s[0] & 0xf0) != 0xe0) RAISE (Bad_UTF8);
          check(s[1]); check(s[2]);
          (*p) += 3;
          return ((s[0] & 0x0f) << 12)
               + ((s[1] & 0x3f) <<  6)
               + ((s[2] & 0x3f));
        } else {
          check(s[1]);
          (*p) += 2;
          return ((s[0] & 0x1f) << 6)
               + ((s[1] & 0x3f));
        }
      }    
      

      【讨论】:

      • 哎呀,很抱歉在 C 代码上浪费了您的时间。但我希望这个小图表对您有所帮助。
      • check() 函数不是坏了吗?不应该测试:if ((c & 0xC0) != 0x80)?另外,RAISE 宏是什么?
      • 感谢 check() 函数中的错误修复。适合我在决赛前一天晚上编写代码。 RAISE 来自cs.princeton.edu/software/cii
      【解决方案3】:

      Scott Reynenconvert UTF-8 into Unicode 写了一个函数。我发现它看着PHP documentation

      function utf8_to_unicode( $str ) {
      
          $unicode = array();        
          $values = array();
          $lookingFor = 1;
      
          for ($i = 0; $i < strlen( $str ); $i++ ) {
              $thisValue = ord( $str[ $i ] );
          if ( $thisValue < ord('A') ) {
              // exclude 0-9
              if ($thisValue >= ord('0') && $thisValue <= ord('9')) {
                   // number
                   $unicode[] = chr($thisValue);
              }
              else {
                   $unicode[] = '%'.dechex($thisValue);
              }
          } else {
                if ( $thisValue < 128) 
              $unicode[] = $str[ $i ];
                else {
                      if ( count( $values ) == 0 ) $lookingFor = ( $thisValue < 224 ) ? 2 : 3;                
                      $values[] = $thisValue;                
                      if ( count( $values ) == $lookingFor ) {
                          $number = ( $lookingFor == 3 ) ?
                              ( ( $values[0] % 16 ) * 4096 ) + ( ( $values[1] % 64 ) * 64 ) + ( $values[2] % 64 ):
                              ( ( $values[0] % 32 ) * 64 ) + ( $values[1] % 64 );
                  $number = dechex($number);
                  $unicode[] = (strlen($number)==3)?"%u0".$number:"%u".$number;
                          $values = array();
                          $lookingFor = 1;
                } // if
              } // if
          }
          } // for
          return implode("",$unicode);
      
      } // utf8_to_unicode
      

      【讨论】:

      • 我写了那个函数。原文在这里,还有一些您可能会觉得有用的其他功能:randomchaos.com/documents/?source=php_and_unicode
      • @Reynen,小世界,嗯?我已经编辑了我的帖子给你信用和一些广告。 ;D
      • 请,哦,请叫它utf8_to_utf16。两者都是“Unicode”,因为两者都是 Unicode 代码点的表示。
      • @ScottReynen 404 链接,你还有原版吗?
      【解决方案4】:

      PHP 代码(假定有效 utf-8,不检查无效 utf-8):

      function ord_utf8($c) {
          $b0 = ord($c[0]);
          if ( $b0 < 0x10 ) {
              return $b0;
              }
          $b1 = ord($c[1]);
          if ( $b0 < 0xE0 ) {
              return (($b0 & 0x1F) << 6) + ($b1 & 0x3F);
              }
          return (($b0 & 0x0F) << 12) + (($b1 & 0x3F) << 6) + (ord($c[2]) & 0x3F);
          }
      

      【讨论】:

      • 非常好,只是当字符已经是 ASCII 时它会失败。 0x10 应该是 0x80,以捕获完整的 ASCII 范围。
      【解决方案5】:

      php &gt;= 7.2 中使用mb_ord()

      或者这个函数:

      function ord_utf8($c) {
          $len = strlen($c);
          $code = ord($c);
          if($len > 1) {
              $code &= 0x7F >> $len;
              for($i = 1; $i < $len; $i++) {
                  $code <<= 6;
                  $code += ord($c[$i]) & 0x3F;
              }
          }
          return $code;
      }
      

      $c 是一个字符。 如果您需要将字符串转换为字符数组。您可以使用它。

      $string = 'abcde';
      $string = preg_split('//u', $string, -1, PREG_SPLIT_NO_EMPTY);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-08-16
        • 2013-12-15
        • 2017-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多