2012-04-08 58 views
29

显然mb_* family中没有mb_trim,所以我试图为我自己实现一个。PHP中的多字节修剪?

我最近发现这个表达式在评论中php.net

/(^\s+)|(\s+$)/u 

所以,我会实现它的方式如下:

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

正则表达式似乎是正确的我,但我对正则表达式极其低调。这将有效地删除任何 Unicode空间在字符串的开始/结束?

+4

看起来像一个无限递归给我... – knittl 2012-04-08 21:27:35

+1

装饰()将删除像字符“\ t,\ r,\ n, \ 0,\ x0B“和\ s修饰符如”\ t,\ r,\ n,\ v,\ f“,所以不是您想要的。要从字符串中删除一些特殊字符,您可以随时使用带第二个参数的trim($ str,$ charlist)。你能写一些你想删除的字符的例子吗? – Naki 2012-04-08 22:11:32

+0

你想删除哪些字符修剪()不会删除? – Niko 2012-04-08 22:15:58

回答

37

标准trim函数可以修剪少量的空格和空格字符。这些被定义为ASCII字符,这意味着某些特定的字节00100 0000

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

这意味着在正确的 UTF-8序列中,字节0xxx xxxx只能引用单字节字符。因此,PHP的trim函数将永远不会修剪掉“半个字符”假设你有一个适当的 UTF-8序列。 (非常非常careful about improper UTF-8 sequences。)


\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) ."]"; 
+0

我想我会坚持'修剪',然后,谢谢 – federicot 2012-04-09 03:46:35

+0

'trim($ s,'')'和trim($ s,'')'工作正常(!)。第二个例子有一个ASCII字符一起工作......所以我们可以说*“'trim()'函数是UTF8安全的”*但不是“'trim()'是ASCII,所以UTF8”。人们会对'/ \ s /'和'/ \ s/u'造成混淆,只有最后一个检测到NBSP。 – 2014-09-08 13:50:43

+1

错!这看起来可能是'trim($ s,'')',但它可以将字符串分解为无效的UTF-8序列。不要使用它! – Wes 2014-11-18 02:21:22

14

我不知道你想用你定义的无限递归函数做什么,但如果你只是想要一个多字节安全的修剪,这将工作。

function mb_trim($str) { 
    return preg_replace("/(^\s+)|(\s+$)/us", "", $str); 
} 
+0

PHP中的pregs是否意识到各种编码?我不记得了,但我知道他们曾经有过一段时间有问题,我认为它在这里。 – Incognito 2012-04-08 23:03:44

+4

@Incognito/u修饰符使preg unicode知道 – 2012-11-08 11:34:48

+0

trim($ s,'')'和trim($ s,'')'正常工作(!)。为什么我们需要'mb_trim()'? – 2014-09-08 13:46:36

2

mb_ereg_replace似乎要解决这个问题:

function mb_trim($str,$regex = "(^\s+)|(\s+$)/us") { 
    return mb_ereg_replace($regex, "", $str); 
} 

..但我不知道有足够的了解正则表达式来知道你的”然后添加人们期望能够馈送到trim()的“charlist”参数 - 即修剪的字符列表 - 所以刚刚将正则表达式作为参数。

这可能是因为你可能有一个特殊字符数组,然后遍历charlist中的每个字符并在构建正则表达式字符串时相应地转义它们。

3

你也可以在UTF-8字符串修剪非ASCII兼容的空间(例如非换空间)与preg_replace('/^\p{Z}+|\p{Z}+$/u','',$str);

\s只会甚至与u修改匹配“ASCII兼容”空格字符
\p{Z}会匹配所有已知的Unicode空格字符

+0

我编辑了@deceze,看到'/ \ s/u',说“只会匹配ASCII码”是错误的(因为 不是ASCII码),你能在你的回答中纠正吗?关于'\ p {Z}',对不起,我没有在我的编辑中引用,很好记(!)。 – 2014-11-19 23:15:22

5

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

function mb_trim ($string, $charlist = null) 
{ 
    if (is_null($charlist)) { 
     return trim ($string); 
    } else { 
     $charlist = str_replace ('/', '\/', preg_quote ($charlist)); 
     return preg_replace ("/(^[$charlist]+)|([$charlist]+$)/us", '', $string); 
    } 
} 

不支持“”的范围虽然。

+1

我喜欢你的方式,但不要忘记preg_quote你的$ charlist :) – 2013-09-04 11:51:23

+0

好的赶上!谢谢。 – 2013-09-04 12:04:33

+1

'mb_trim('000foo000','0')'......失败:-3 – deceze 2013-12-04 15:54:26

3

好吧,我拿了@ edson-medina的解决方案,修正了一个bug并添加了一些单元测试。以下是我们用来给MB对手修剪,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); 
    } 
} 
//////////////////////////////////////////////////////////////////////////////////// 

这里的单元测试中,我写了有兴趣的人:

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', ' Å')); 
}