【问题标题】:UTF-8 safe equivalent of ord or charCodeAt() in PHPUTF-8 安全等效于 PHP 中的 ord 或 charCodeAt()
【发布时间】:2012-05-07 04:00:08
【问题描述】:

我需要能够使用 ord() 来获得与 javascript 的 charCodeAt() 函数相同的值。问题是 ord() 不支持 UTF8。

如何让 Ą 在 PHP 中转换为 260?我已经尝试了一些 uniord 函数,但它们都报告 256 而不是 260。

非常感谢您的帮助!

问候

【问题讨论】:

  • @bardiir 是的,我在发帖后意识到这一点。
  • 对不起,应该更清楚。 PHP

标签: php javascript utf-8 character-encoding


【解决方案1】:

ord() 按字节工作(与大多数 PHP 标准字符串函数一样——如果不是全部的话)。您需要自己转换它,例如在多字节字符串扩展的帮助下:

$utf8Character = 'Ą';
list(, $ord) = unpack('N', mb_convert_encoding($utf8Character, 'UCS-4BE', 'UTF-8'));
echo $ord; # 260

【讨论】:

  • list 不是函数,而是特殊形式; list($ord) = $someArray$ord = $someArray[0] 基本相同。 list 当您想将数组的元素分配给多个变量时非常方便,或者要解决您无法在 PHP
  • 啊,我明白了。但是当我执行上面的代码时,它没有输出任何东西(它是空白的)。任何想法如何将其变成本垒打?
  • 不确定为什么它不适用于list,但试试这个:$chars = unpack('N', mb_convert_encoding($utf8Character, 'UCS-4BE', 'UTF-8')); $ord = $chars[0];
  • 嗯,也不行。看起来 unpack 和 list 都在数组的索引 1 中设置值而不是 0。这会根据字符占用的字节数而改变,还是总是可靠地为 1?
  • print_r 给了我 Array ( [1] => 260 ),这很奇怪,它不是零索引,但只要它有效,我就很高兴 :) 谢谢!
【解决方案2】:

试试:


function uniord($c) {
        $h = ord($c{0});
        if ($h <= 0x7F) {
            return $h;
        } else if ($h < 0xC2) {
            return false;
        } else if ($h <= 0xDF) {
            return ($h & 0x1F) << 6 | (ord($c{1}) & 0x3F);
        } else if ($h <= 0xEF) {
            return ($h & 0x0F) << 12 | (ord($c{1}) & 0x3F) << 6
                                     | (ord($c{2}) & 0x3F);
        } else if ($h <= 0xF4) {
            return ($h & 0x0F) << 18 | (ord($c{1}) & 0x3F) << 12
                                     | (ord($c{2}) & 0x3F) << 6
                                     | (ord($c{3}) & 0x3F);
        } else {
            return false;
        }
    }
    echo uniord('Ą');

【讨论】:

  • 感谢 Sudhir,这行得通!这个函数的来源是什么?
  • 好吧,其实我也是从某个来源得到的>
  • 手工实现 UTF-8 很有趣;我已经做过几次了。但我认为使用别人维护的库更聪明。特别是从那时起,您也可以处理其他编码......
【解决方案3】:

mbstring 版本:

function utf8_char_code_at($str, $index)
{
    $char = mb_substr($str, $index, 1, 'UTF-8');

    if (mb_check_encoding($char, 'UTF-8')) {
        $ret = mb_convert_encoding($char, 'UTF-32BE', 'UTF-8');
        return hexdec(bin2hex($ret));
    } else {
        return null;
    }
}

使用 htmlspecialchars 和 htmlspecialchars_decode 获取一个字符:

function utf8_char_code_at($str, $index)
{
    $char = '';
    $str_index = 0;

    $str = utf8_scrub($str);
    $len = strlen($str);

    for ($i = 0; $i < $len; $i += 1) {

        $char .= $str[$i];

        if (utf8_check_encoding($char)) {

            if ($str_index === $index) {
                return utf8_ord($char);
            }

            $char = '';
            $str_index += 1;
        }
    }

    return null;
}

function utf8_scrub($str)
{
    return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, 'UTF-8'));
}

function utf8_check_encoding($str)
{
    return $str === utf8_scrub($str);
}

function utf8_ord($char)
{
    $lead = ord($char[0]);

    if ($lead < 0x80) {
        return $lead;
    } else if ($lead < 0xE0) {
        return (($lead & 0x1F) << 6) 
      | (ord($char[1]) & 0x3F);
    } else if ($lead < 0xF0) {
        return (($lead &  0xF) << 12)
     | ((ord($char[1]) & 0x3F) <<  6)
     |  (ord($char[2]) & 0x3F);
    } else {
        return (($lead &  0x7) << 18)
     | ((ord($char[1]) & 0x3F) << 12)
     | ((ord($char[2]) & 0x3F) <<  6)
     |  (ord($char[3]) & 0x3F);
    }
}

PHP 扩展版本:

#include "ext/standard/html.h"
#include "ext/standard/php_smart_str.h"

const zend_function_entry utf8_string_functions[] = {
    PHP_FE(utf8_char_code_at, NULL)
    PHP_FE_END
};

PHP_FUNCTION(utf8_char_code_at)
{
    char *str;
    int len;
    long index;

    unsigned int code_point;
    long i;
    int status;
    size_t pos = 0, old_pos = 0;

    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "sl", &str, &len, &index) == FAILURE) {
        return;
    }

    for (i = 0; pos < len; ++i) {
        old_pos = pos;
        code_point = php_next_utf8_char((const unsigned char *) str, (size_t) len, &pos, &status);

        if (i == index) {
            if (status == SUCCESS) {
                RETURN_LONG(code_point);
            } else {
                RETURN_NULL();
            }

        }

    }

    RETURN_NULL();
}

【讨论】:

  • 哇。这就像 insanely 复杂的东西,应该是语言本身的微不足道的内置!我会给你一个 +1 的努力,但是哇,哇!
  • 谢谢。我添加了另一个使用 htmlspecialchars 和 htmlspecialchars_decode 的示例。我发帖是为了阅读 PHP 源代码和练习 C 语言。我正在考虑为 mbstring 或 PHP 核心提出新的字符串函数。这个函数对应于 Ruby 的 each_char。该函数可用于定义回退函数,例如 mb_strlen 和 mb_substr。我将此功能实现为 PHP 扩展:blog.sarabande.jp/post/57645700697(对于日语文章感到抱歉)。
【解决方案4】:

这应该等同于基于 @hakre 工作的 JavaScript 的 charCodeAt(),但更正为实际上与 JavaScript 一样工作(以我能想到的各种方式进行测试):

function charCodeAt($string, $offset) {
  $string = mb_substr($string, $offset, 1);
  list(, $ret) = unpack('S', mb_convert_encoding($string, 'UTF-16LE'));
  return $ret;
}

(这需要安装并激活 PHP 扩展“mbstring”。)

【讨论】:

  • 用 $string 改变 $character :-)
【解决方案5】:

这里有一个 ord_utf8 函数: https://stackoverflow.com/a/42600959/7558876

这个函数看起来像这样(接受字符串并返回整数)

<?php

function ord_utf8($s){
return (int) ($s=unpack('C*',$s[0].$s[1].$s[2].$s[3]))&&$s[1]<(1<<7)?$s[1]:
($s[1]>239&&$s[2]>127&&$s[3]>127&&$s[4]>127?(7&$s[1])<<18|(63&$s[2])<<12|(63&$s[3])<<6|63&$s[4]:
($s[1]>223&&$s[2]>127&&$s[3]>127?(15&$s[1])<<12|(63&$s[2])<<6|63&$s[3]:
($s[1]>193&&$s[2]>127?(31&$s[1])<<6|63&$s[2]:0)));
}

还有一个快速的chr_utf8https://stackoverflow.com/a/42510129/7558876

这个函数看起来像这样(接受整数并返回一个字符串)

<?php

function chr_utf8($n,$f='C*'){
return $n<(1<<7)?chr($n):($n<1<<11?pack($f,192|$n>>6,1<<7|191&$n):
($n<(1<<16)?pack($f,224|$n>>12,1<<7|63&$n>>6,1<<7|63&$n):
($n<(1<<20|1<<16)?pack($f,240|$n>>18,1<<7|63&$n>>12,1<<7|63&$n>>6,1<<7|63&$n):'')));
}

如果您想要一个示例,请查看链接...

【讨论】:

  • 欢迎提供解决方案的链接,但请确保您的答案在没有它的情况下有用:add context around the link 这样您的其他用户就会知道它是什么以及为什么会出现,然后引用最相关的内容您链接到的页面的一部分,以防目标页面不可用。 Answers that are little more than a link may be deleted.
  • 顺便说一句:如果您认为该问题在 Stack Overflow 上的其他地方有答案,请将其标记为 duplicate,而不是引用其他答案。
猜你喜欢
  • 2016-06-05
  • 1970-01-01
  • 2012-12-15
  • 1970-01-01
  • 2021-09-03
  • 2014-05-16
  • 1970-01-01
  • 2011-04-22
  • 2010-12-04
相关资源
最近更新 更多