PHP 中 ord 或 charCodeAt() 的 UTF-8 安全等价物

IT技术 php javascript utf-8 character-encoding
2021-02-13 11:27:48

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

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

非常感谢您的帮助!

问候

5个回答

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();
}
哇。对于应该在语言中内置的微不足道的东西来说,这就像疯狂地复杂!我会给你+1的努力,但是哇,哇!
2021-03-17 11:27:48
谢谢。我使用 htmlspecialchars 和 htmlspecialchars_decode 添加了另一个示例。我发帖是为了阅读PHP源代码和练习C语言。我正在考虑为 mbstring 或 PHP 核心提出新的字符串函数。这个函数对应于Ruby 的each_char。此函数可用于定义回退函数,例如 mb_strlen 和 mb_substr。我将此功能实现为 PHP 扩展名:blog.sarabande.jp/post/57645700697(抱歉日语文章)。
2021-04-13 11:27:48

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

$utf8Character = 'Ą';
list(, $ord) = unpack('N', mb_convert_encoding($utf8Character, 'UCS-4BE', 'UTF-8'));
echo $ord; # 260
啊,我明白了。但是当我执行上面的代码时,它没有输出任何内容(它是空白的)。任何想法如何将其变成本垒打?
2021-03-18 11:27:48
不知道为什么它不能使用list,但试试这个: $chars = unpack('N', mb_convert_encoding($utf8Character, 'UCS-4BE', 'UTF-8')); $ord = $chars[0];
2021-04-03 11:27:48
list不是函数,而是一种特殊的形式;list($ord) = $someArray基本上是一样的东西$ord = $someArray[0]list当您想将数组的元素分配给多个变量时,或者要避免无法向数组表达式添加下标(在 PHP < 5.4 中不是实际数组变量)这一事实时,这很方便。
2021-04-04 11:27:48
嗯,也不行。看起来 unpack 和 list 都在数组的索引 1 中设置值而不是 0。这会根据字符占用的字节数而改变还是总是可靠的 1?
2021-04-04 11:27:48
print_r 给了我 Array ( [1] => 260 ),这很奇怪,它不是零索引,但只要它有效,我就很高兴:) 谢谢!
2021-04-08 11:27:48

尝试:


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('Ą');
好吧,实际上我也是从某个来源得到它的,目前我不记得了,因为我有这段代码很久了,抱歉,但我希望该功能可以帮助您解决问题
2021-03-19 11:27:48
手动实现 UTF-8 很有趣。我已经做过几次了。但我认为使用其他人维护的库更明智。尤其是从那时起,您也可以处理其他编码。
2021-03-27 11:27:48
谢谢 Sudhir,那行得通!这个函数的来源是什么?
2021-03-29 11:27:48

这应该等同于charCodeAt()基于@hakre 工作的JavaScript ,但已更正为实际工作与 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 :-)
2021-03-22 11:27:48

这里有一个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):'')));
}

如果你想要一个例子,请检查链接......

欢迎提供指向解决方案的链接,但请确保您的答案在没有它的情况下也有用:在链接周围添加上下文,以便您的其他用户了解它是什么以及它为什么在那里,然后引用您页面中最相关的部分“重新链接,以防目标页面不可用。仅是链接的答案可能会被删除。
2021-03-23 11:27:48
顺便说一句:如果您认为该问题在 Stack Overflow 的其他地方有答案,请将其标记为重复而不是引用其他答案。
2021-04-06 11:27:48