Многобитовая обрезка в PHP?
По-видимому, там нет mb_trim
в mb_*
семье, поэтому я пытаюсь реализовать его для себя.
Я недавно нашел это регулярное выражение в комментарии в 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);
}
}
Регулярное выражение кажется правильным для меня, но я чрезвычайно noob с регулярными выражениями. Будет ли это эффективно удалять любое Unicode-пространство в начале/конце строки?
Ответы
Ответ 1
Стандартная функция trim
обрезает несколько пространственных и пространственных символов. Они определены как символы ASCII, что означает определенные конкретные байты от 0
до 0100 0000
.
Правильный Вход UTF-8 никогда не будет содержать многобайтовых символов, состоящих из байтов 0xxx xxxx
. Все байты в правильных многобайтовых символах UTF-8 начинаются с 1xxx xxxx
.
Это означает, что в последовательности правильной UTF-8 байты 0xxx xxxx
могут ссылаться только на однобайтные символы. Поэтому функция PHP trim
никогда не убирает "половину символа" , предполагая, что у вас есть последовательность правильная UTF-8. (Будьте очень осторожны осторожно о ненадлежащих последовательностях UTF-8.)
\s
в регулярных выражениях ASCII будут в основном соответствовать тем же символам, что и trim
.
Функции preg
с модификатором /u
работают только с регулярными выражениями, закодированными в UTF-8, и /\s/u
соответствуют также UTF8 nbsp. Это поведение с неразрывными пробелами является единственным преимуществом его использования.
Если вы хотите заменить символы пробела в других кодировках, не поддерживающих ASCII, ни один из них не будет работать.
Другими словами, если вы пытаетесь обрезать обычные пространства ASCII-совместимой строкой, просто используйте trim
. При использовании /\s/u
будьте осторожны со значением nbsp для вашего текста.
Будьте осторожны:
$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) ."]";
Ответ 2
Я не знаю, что вы пытаетесь сделать с этой бесконечной рекурсивной функцией, которую вы определяете, но если вы просто хотите многобилетную защиту, это будет работать.
function mb_trim($str) {
return preg_replace("/(^\s+)|(\s+$)/us", "", $str);
}
Ответ 3
Эта версия поддерживает второй необязательный параметр $ 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);
}
Не поддерживает ".." для диапазонов, хотя.
Ответ 4
Хорошо, поэтому я принял решение @edson-medina и исправил ошибку и добавил некоторые модульные тесты. Здесь три функции, которые мы используем, чтобы предоставить mb-копии для обрезки, rtrim и ltrim.
////////////////////////////////////////////////////////////////////////////////////
//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
Вы также можете обрезать не-ascii-совместимые пространства (например, нераспространяющееся пространство) в строках UTF-8 с помощью preg_replace('/^\p{Z}+|\p{Z}+$/u','',$str);
\s
будет соответствовать только символу пробела "ascii compatible" даже с модификатором u
.
но \p{Z}
будет соответствовать всем известным символам пробела unicode
Ответ 6
mb_ereg_replace, похоже, обойти это:
function mb_trim($str,$regex = "(^\s+)|(\s+$)/us") {
return mb_ereg_replace($regex, "", $str);
}
.. но я не знаю достаточно о регулярных выражениях, чтобы узнать, как вы добавляете в параметр "charlist", которые люди ожидали бы, чтобы иметь возможность комбинировать с trim() - то есть список символов для обрезки - поэтому просто запустили параметр regex.
Возможно, у вас может быть массив специальных символов, а затем выполнить его для каждого символа в charlist и избежать их соответственно при создании строки регулярного выражения.
Ответ 7
Мои два цента
Фактическое решение вашего вопроса состоит в том, что вы должны сначала выполнить проверки кодировки, прежде чем работать над изменением внешних входных строк. Многие из них быстро узнают о "санации и проверке" входных данных, но не спешат освоить шаг определения базовой природы (кодировки символов) строк, с которыми они работают на ранних этапах.
Сколько байтов будет использовано для представления каждого символа? При правильно отформатированном UTF-8 он может составлять 1 (trim
символов имеет дело с), 2, 3 или 4 байта. Проблема возникает, когда в игру вступают устаревшие или искаженные представления UTF-8 - границы байтовых символов могут не совпадать, как ожидалось (говорят дилетанты).
В PHP некоторые рекомендуют, чтобы все строки были вынуждены соответствовать правильной кодировке UTF-8 (1, 2, 3 или 4 байта на символ), где такие функции, как trim()
все еще будут работать, потому что граница байта/символа для символы, с которыми он имеет дело, будут согласованы с расширенными ASCII/1-байтовыми значениями, которые trim()
стремится исключить из начала и конца строки (страница справочника trim).
Однако, поскольку компьютерное программирование является разнообразной областью, невозможно иметь общий подход, который работает во всех сценариях. С учетом вышесказанного, напишите ваше приложение так, как оно должно быть для правильной работы. Просто делаете базовый веб-сайт, управляемый базой данных, с вводом формы? Да, за мои деньги заставь все быть UTF-8.
Примечание. Проблемы с интернационализацией по-прежнему будут возникать, даже если проблема с UTF-8 остается стабильной. Зачем? Многие неанглийские наборы символов существуют в 2, 3 или 4-байтовом пространстве (кодовые точки и т.д.). Очевидно, что если вы используете компьютер, который должен работать со скриптами на китайском, японском, русском, арабском или иврите, вы хотите, чтобы все работало с 2, 3 и 4 байтами! Помните, что PHP trim
функция может обрезать символы по умолчанию, или указанный пользователем из них. Это имеет значение, особенно если вам нужна ваша trim
для учета некоторых китайских иероглифов.
Я бы предпочел иметь дело с проблемой невозможности доступа к моему сайту, а затем с проблемой доступа и ответов, которые не должны появляться. Когда вы думаете об этом, это соответствует принципам наименьших привилегий (безопасность) и универсального дизайна (доступность).
Резюме
Если входные данные не будут соответствовать надлежащей кодировке UTF-8, вы можете вызвать исключение. Вы можете попытаться использовать многобайтовые функции PHP для определения вашей кодировки или другую многобайтовую библиотеку. Если и когда PHP написан для полной поддержки Unicode (Perl, Java...), PHP будет лучше для него. Усилия по созданию Unicode в PHP прекратились несколько лет назад, поэтому вы вынуждены использовать дополнительные библиотеки для разумной работы с многобайтовыми строками UTF-8. Просто добавление флага /u
в preg_replace()
не смотрит на общую картину.
Обновить:
При этом я полагаю, что следующая многобайтовая обрезка будет полезна для тех, кто пытается извлечь ресурсы REST из компонента пути URL-адреса (естественно, за исключением строки запроса. Примечание: это будет полезно после очистки и проверки строки пути).
function mb_path_trim($path)
{
return preg_replace("/^(?:\/)|(?:\/)$/u", "", $path);
}