PHP中的多字节修剪?

fed*_*o-t 34 php regex unicode multibyte mbstring

显然没有mb_trimmb_*家庭,所以我想实现一个我自己.

我最近在php.net的评论中发现了这个正则表达式:

/(^\s+)|(\s+$)/u
Run Code Online (Sandbox Code Playgroud)

所以,我将通过以下方式实现它:

function multibyte_trim($str)
{
    if (!function_exists("mb_trim") || !extension_loaded("mbstring")) {
        return preg_replace("/(^\s+)|(\s+$)/u", "", $str);
    } else {
        return mb_trim($str);
    }
}
Run Code Online (Sandbox Code Playgroud)

正则表达式似乎对我来说是正确的,但我是正则表达式的极力菜鸟.这会有效地删除字符串开头/结尾的任何 Unicode空间吗?

dec*_*eze 45

标准trim功能修剪了一些空间和类似空间的角色.这些被定义为ASCII字符,这意味着某些特定字节00100 0000.

正确的 UTF-8输入永远不会包含由字节组成的多字节字符0xxx xxxx.正确的 UTF-8多字节字符中的所有字节都以1xxx xxxx.

这意味着在正确的 UTF-8序列中,字节0xxx xxxx只能引用单字节字符.trim因此,假设您有一个正确的 UTF-8序列,PHP的函数将永远不会删除"半个字符" .(非常小心不正确的 UTF-8序列.)


\s 对ASCII正则表达式将大致相同的字符相匹配的trim.

preg具有/u修饰符的函数仅适用于UTF-8编码的正则表达式,并且/\s/u还匹配UTF8的nbsp.具有不间断空格的这种行为是使用它的唯一优势.

如果要替换其他非ASCII兼容编码中的空格字符,则这两种方法都不起作用.

换句话说,如果你试图修剪常用空格和ASCII兼容的字符串,只需使用trim.使用时请/\s/u注意文本的含义.


照顾自己:

  $s1 = html_entity_decode(" Hello   "); // the NBSP
  $s2 = "  exotic test ?  ";

  echo "\nCORRECT trim: [". trim($s1) ."], [".  trim($s2) ."]";
  echo "\nSAME: [". trim($s1) ."] == [". preg_replace('/^\s+|\s+$/','',$s1) ."]";
  echo "\nBUT: [". trim($s1) ."] != [". preg_replace('/^\s+|\s+$/u','',$s1) ."]";

  echo "\n!INCORRECT trim: [". trim($s2,' ') ."]"; // DANGER! not UTF8 safe!
  echo "\nSAFE ONLY WITH preg: [". 
       preg_replace('/^[\s]+|[\s]+$/u', '', $s2) ."]";
Run Code Online (Sandbox Code Playgroud)

  • 实际上,从UTF-8字符串中修剪ASCII字符是安全的,但是从字符串中删除UTF-8字符则不然.那是因为`trim`不理解""是一个字符,而是三个字节,它会在遇到时将这三个字节中的任何一个单独调整*.@Peter (3认同)
  • 错误的!这似乎可以正常工作`trim($s,'')`,但它可以将字符串分解为无效的 UTF-8 序列。不要使用它! (2认同)
  • 抱歉 - 如果没有完整的测试,说“工作正常”是错误的,你说“`trim($s,$utf8)` 是错误的!”是正确的!–,我建议在答案的文本中这么说。关于我的其他评论,我认为答案的文本“`\s`将主要匹配相同的字符”*是错误的*:请自行测试 `preg_replace('/\s/u', '',$s)`当 `$s = html_entity_decode(" Hello   ");` 计算 UTF8 [NBSP](https://en.wikipedia.org/wiki/Non-breaking_space) 时。 (2认同)
  • 坚持使用不支持 utf8 的 trim() 是一种解决方案,前提是您要删除的所有字符都是一字节字符。但是,例如,如果您还想删除一些多字节字符(例如 U+200B,“零宽度空间”),您需要适当的多字节扩展 trim,这正是 OP 所要求的。 (2认同)

kba*_*kba 20

我不知道你正在尝试用你定义的无限递归函数做什么,但如果你只想要一个多字节安全的修剪,这将有效.

function mb_trim($str) {
  return preg_replace("/(^\s+)|(\s+$)/us", "", $str); 
}
Run Code Online (Sandbox Code Playgroud)

  • @Incognito/u修饰符使preg unicode知道 (5认同)

Eds*_*ina 6

此版本支持第二个可选参数$ charlist:

function mb_trim ($string, $charlist = null) 
{   
    if (is_null($charlist)) {
        return trim ($string);
    } 

    $charlist = str_replace ('/', '\/', preg_quote ($charlist));
    return preg_replace ("/(^[$charlist]+)|([$charlist]+$)/us", '', $string);
}
Run Code Online (Sandbox Code Playgroud)

但是不支持范围"..".

  • 我喜欢您的方式,但不要忘记preg_quote您的$ charlist :) (2认同)

Mic*_*art 6

好的,所以我采用了@ edson-medina的解决方案并修复了一个错误并添加了一些单元测试.这是我们用来为mb对应物赋予trim,rtrim和ltrim的3个函数.

////////////////////////////////////////////////////////////////////////////////////
//Add some multibyte core functions not in PHP
////////////////////////////////////////////////////////////////////////////////////
function mb_trim($string, $charlist = null) {
    if (is_null($charlist)) {
        return trim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/(^[$charlist]+)|([$charlist]+$)/us", '', $string);
    }
}
function mb_rtrim($string, $charlist = null) {
    if (is_null($charlist)) {
        return rtrim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/([$charlist]+$)/us", '', $string);
    }
}
function mb_ltrim($string, $charlist = null) {
    if (is_null($charlist)) {
        return ltrim($string);
    } else {
        $charlist = preg_quote($charlist, '/');
        return preg_replace("/(^[$charlist]+)/us", '', $string);
    }
}
////////////////////////////////////////////////////////////////////////////////////
Run Code Online (Sandbox Code Playgroud)

这是我为感兴趣的人写的单元测试:

public function test_trim() {
    $this->assertEquals(trim(' foo '), mb_trim(' foo '));
    $this->assertEquals(trim(' foo ', ' o'), mb_trim(' foo ', ' o'));
    $this->assertEquals('foo', mb_trim(' Åfoo? ', ' Å?'));
}

public function test_rtrim() {
    $this->assertEquals(rtrim(' foo '), mb_rtrim(' foo '));
    $this->assertEquals(rtrim(' foo ', ' o'), mb_rtrim(' foo ', ' o'));
    $this->assertEquals('foo', mb_rtrim('foo? ', ' ?'));
}

public function test_ltrim() {
    $this->assertEquals(ltrim(' foo '), mb_ltrim(' foo '));
    $this->assertEquals(ltrim(' foo ', ' o'), mb_ltrim(' foo ', ' o'));
    $this->assertEquals('foo', mb_ltrim(' Åfoo', ' Å'));
}
Run Code Online (Sandbox Code Playgroud)


Opt*_*pty 5

您还可以修剪 UTF-8 字符串上的非 ascii 兼容空格(例如不间断空格),即使使用修饰符preg_replace('/^\p{Z}+|\p{Z}+$/u','',$str);

\s也只会匹配“ascii 兼容”空格字符。 但会匹配所有已知的 unicode 空格字符u
\p{Z}