Подтвердить что ты не робот

Многобитовая обрезка в 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-пространство в начале/конце строки?

4b9b3361

Ответ 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);
}