显然mb_*
family中没有mb_trim
,所以我正在尝试为自己实现一个。{/ p>
我最近在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空间吗?
答案 0 :(得分:45)
标准trim
功能修剪了一些空格和空格字符。这些被定义为ASCII字符,这意味着从0
到0100 0000
的某些特定字节。
正确 UTF-8输入永远不会包含由字节0xxx xxxx
组成的多字节字符。 正确 UTF-8多字节字符中的所有字节均以1xxx xxxx
开头。
这意味着在正确的 UTF-8序列中,字节0xxx xxxx
只能引用单字节字符。因此,PHP的trim
函数永远不会删除“半个字符”假设你有一个正确的 UTF-8序列。 (非常非常careful about improper UTF-8 sequences。)
ASCII正则表达式上的\s
主要与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) ."]";
答案 1 :(得分:18)
我不知道你要用你定义的无限递归函数做什么,但如果你只想要一个多字节安全的修剪,这将有效。
function mb_trim($str) {
return preg_replace("/(^\s+)|(\s+$)/us", "", $str);
}
答案 2 :(得分: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);
}
虽然不支持范围“..”。
答案 3 :(得分:4)
您还可以使用preg_replace('/^\p{Z}+|\p{Z}+$/u','',$str);
修剪UTF-8字符串上的非ascii兼容空格(例如,不间断空格)
\s
只会匹配“ascii compatible”空格字符,即使使用u
修饰符。
但是\p{Z}
将匹配所有已知的unicode空格字符
答案 4 :(得分:4)
好的,所以我采用了@ edson-medina的解决方案并修复了一个错误,并添加了一些单元测试。这是我们用来为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', ' Å'));
}
答案 5 :(得分:2)
mb_ereg_replace似乎可以解决这个问题:
function mb_trim($str,$regex = "(^\s+)|(\s+$)/us") {
return mb_ereg_replace($regex, "", $str);
}
..但我对正则表达式知之甚少,不知道你如何添加人们希望能够提供给trim()的“charlist”参数 - 即要修剪的字符列表 - 所以刚刚使用正则表达式作为参数。
可能你可能有一个特殊字符数组,然后为charlist中的每个字符单步执行它,并在构建正则表达式字符串时相应地转义它们。
答案 6 :(得分:1)
(从trim
上的重复Q移植而来,与NBSP格格不入。)以下注释自PHP 7.2+起有效。里程可能因早期版本而异(请在评论中注明)。
PHP trim
会忽略不间断空格。它仅修剪基本ASCII范围内的空格。作为参考,the source code的修边内容如下(即,没有未记录的带有修边的功能):
(c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == '\v' || c == '\0')
除上面的普通空格(ASCII 32,
)外,这些都是ASCII控制字符; LF(10:\n
),CR(13:\r
),HT(9:\t
),VT(11:\v
),NUL(0:{{ 1}})。 (请注意,在PHP中,必须将转义的字符双引号:\0
等。否则,它们将被解析为文字"\n", "\t"
等。)
以下是使用\n
的{{1}}(trim
,ltrim
,rtrim
)的三种风格的简单实现,它们与Unicode字符串一起使用:
trim
随时将它们包装到您自己的preg_replace
函数中。
对于每个PCRE specification,启用preg_replace('~^\s+~u', '', $string) // == ltrim
preg_replace('~\s+$~u', '', $string) // == rtrim
preg_replace('~^\s+|\s+$~us', '', $string) // == trim
Unicode模式的mb_*trim
“任何空格”转义序列字符将与以下所有空格字符匹配:
\s
您可以看到u
中的test iteration带有The horizontal space characters are:
U+0009 Horizontal tab (HT)
U+0020 Space
U+00A0 Non-break space
U+1680 Ogham space mark
U+180E Mongolian vowel separator
U+2000 En quad
U+2001 Em quad
U+2002 En space
U+2003 Em space
U+2004 Three-per-em space
U+2005 Four-per-em space
U+2006 Six-per-em space
U+2007 Figure space
U+2008 Punctuation space
U+2009 Thin space
U+200A Hair space
U+202F Narrow no-break space
U+205F Medium mathematical space
U+3000 Ideographic space
The vertical space characters are:
U+000A Linefeed (LF)
U+000B Vertical tab (VT)
U+000C Form feed (FF)
U+000D Carriage return (CR)
U+0085 Next line (NEL)
U+2028 Line separator
U+2029 Paragraph separator
Unicode标志来处理所有列出的空格。它们均按照PCRE规范按预期进行修整。如果您只定位上方的水平空间,则preg_replace
会与它们匹配,就像u
会匹配所有垂直空间一样。
在某些答案中使用\h
可能会因某些原因而失败;特别是,对于大多数ASCII空间,以及令人震惊的蒙古元音分隔符。忽必烈会生气。以下是\v
遗漏的列表:U + 0009 “水平”标签(HT),U + 000A 换行符(LF),U + 000C 表格feed(FF),U + 000D 回车(CR),U + 0085 下一行(NEL)和U + 180E 蒙古语元音分隔符。
关于发生这种情况的原因,以上PCRE规范还指出:“ \p{Z}
与\p{Z}
或\s
或\p{Z}
匹配的任何字符”。也就是说,\h
是\v
的超集。然后,只需使用\s
代替\p{Z}
。对于阅读您的代码的人来说,它可能更全面,并且导入更直接,而他们可能不会记住所有字符类型的缩写。
答案 7 :(得分:0)
您的问题的实际解决方案是,在更改外部输入字符串之前,您应该首先进行编码检查。许多人很快就了解了“消毒和验证”输入数据的知识,但是却很快学会了识别早期使用的字符串的基础性质(字符编码)的步骤。
将使用多少个字节表示每个字符?使用正确格式的UTF-8,它可以是1(2个字符,trim
处理),2个,3个或4个字节。问题是当旧的或格式错误的UTF-8表示形式发挥作用时出现的-字节字符边界可能未按预期排列(外行说话)。
在PHP中,有人主张应强制所有字符串遵循正确的UTF-8编码(每个字符1、2、3或4个字节),其中trim()
之类的功能仍将起作用,因为该字节trim()
试图从字符串(trim manual page的开头和结尾消除)的扩展ASCII / 1字节值将与它处理的字符的/字符边界完全一致。
但是,由于计算机编程是一个多元化的领域,因此不可能有一种适用于所有情况的通用方法。话虽如此,编写应用程序的方式必须使其正常运行。只是使用表单输入做一个基本的数据库驱动的网站? 是,因为我的钱迫使一切都变成UTF-8。
注意:即使您的UTF-8问题稳定,您仍然会遇到国际化问题。为什么? 2、3或4字节空间(代码点等)中存在许多非英语字符集。显然,如果您使用的计算机必须处理中文,日文,俄文,阿拉伯文或希伯来文脚本,那么您还希望所有内容都使用2、3和4个字节!请记住,PHP trim
函数可以修剪默认字符或用户指定的字符。这很重要,尤其是在您需要trim
来解释一些汉字的情况下。
我宁愿处理无法访问我的网站的问题,也不应该处理访问和响应问题。当您考虑时,这符合最低特权(安全性)和通用设计(可访问性)的原则。
如果输入数据不符合正确的UTF-8编码,则可能需要throw an exception。您可以尝试使用PHP multi-byte functions来确定您的编码或其他一些多字节库。如果何时以及何时编写PHP以完全支持unicode(Perl,Java ...),则PHP会更好。 PHP unicode的工作在几年前就死了,因此您被迫使用额外的库来理智地处理UTF-8多字节字符串。只是将/u
标志添加到preg_replace()
并不能看到全局。
更新:
话虽如此,我相信以下多字节修剪对那些尝试从url的路径组件中提取REST资源(自然会减少查询字符串)的人很有用。注意:在清理和验证路径之后,这将很有用字符串。
function mb_path_trim($path)
{
return preg_replace("/^(?:\/)|(?:\/)$/u", "", $path);
}