2012-04-08 39 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', ' Å')); 
}