Вопрос по regex, unicode, php – Многобайтовая обрезка в PHP?

32

По-видимому, нетmb_trim вmb_* familyпоэтому я пытаюсь реализовать один для себя.

Я недавно нашел это регулярное выражение в комментарии в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);
    }
}

Регулярное выражение кажется мне верным, но я крайне неравнодушен к регулярным выражениям. Будет ли это эффективно удалитьany Unicode пробел в начале / конце строки?

Выглядит как бесконечная рекурсия для меня & # x2026; knittl
я думаю, что ваше регулярное выражение соответствует 1 или более пробелов в начале или конце строки Robbie
Какие символы вы хотите удалить, что trim () не удаляет? Niko
trim () удалит такие символы, как & quot; , \ Т, \ г, \ п, \ 0, \ x0B & Quot; и модификатор \ s, такой как & quot; , \ Т, \ г, \ п, \ v, \ F & Quot; так что это не то, что Ты хочешь, я думаю. Чтобы удалить некоторые специальные символы из строки, вы всегда можете использовать trim ($ str, $ charlist) со вторым параметром. Можете ли вы написать несколько примеров символов, которые хотите удалить? Naki
@ knittl, да, ты прав! Не понял этого. Функция, которую я объявляю, должна иметь другое имя. Я просто проверял, если в будущемmb_trim функция добавлена кmbstring расширение, и использование этого вместо моего собственного federicot

Ваш Ответ

7   ответов
2

mb_ereg_replace, кажется, обходит это:

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

... но я не знаю достаточно о регулярных выражениях, чтобы знать, как вы потом добавите их в "charlist" люди с параметрами могут ожидать, что они смогут передавать в trim () - то есть список символов для усечения - так что просто сделали регулярное выражение параметром.

Возможно, вы могли бы иметь массив специальных символов, а затем выполнить его для каждого символа в списке символов и, соответственно, экранировать их при построении строки регулярного выражения.

44

trim Функция обрезает несколько пробелов и пробелов. Они определены как символы ASCII, что означает определенные конкретныеbytes от0 в0100 0000.

Proper UTF-8 никогда не будет содержать многобайтовые символы, состоящие из байтов0xxx xxxx, Все байты вproper Многобайтовые символы UTF-8 начинаются с1xxx xxxx.

Это означает, что вproper Последовательность UTF-8, байты0xxx xxxx может ссылаться только на однобайтовые символы. РНР & APOS; strim поэтому функция никогда не обрезает "половину символа"assuming у тебя естьproper Последовательность UTF-8. (Будь очень оченьосторожноimproper UTF-8 sequences.)

\s  в ASCII регулярные выражения будут в основном соответствовать тем же символам, что иtrim.

preg функции с/u модификатор работает только наUTF-8 encoded regular expressions, а также/\s/u соответствуют также UTF8 'sNBSP, Такое поведение с неразрывными пробелами является единственным преимуществом его использования.

Если вы хотите заменить пробелы в других, не 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) ."]";
неправильно! может показаться, что это работаетtrim($s,'') но он может разбить строку на недопустимую последовательность UTF-8. не используйте его!
Действительно, обрезка символов ASCII от строки UTF-8 безопасна, но обрезка символов UTF-8 от строки - нет. Это потому, чтоtrim "не понимает" & quot; быть одним символом, но тремя байтами, и он урежет любой из этих трех байтовindividually когда встретил @Питер
Я думаю, что я буду придерживатьсяtrim тогда спасибо federicot
trim($s,'') а такжеtrim($s,' ') работает отлично (!). Во втором примере ASCII-символ работает вместе ... Итак, мы можем сказать,"trim() function is UTF8 safe" но не & quot;trim() это ASCII, так же как и UTF8 ". Люди путают/\s/ а также/\s/u где только последний обнаруживает NBSP.
К сожалению & # x2013; неверно говорить "работай нормально" без полного теста вы правильно скажете & quot;trim($s,$utf8) неправильно! & quot; & # x2013 ;, я предлагаю сказать это в тексте ответа. Что касается моего другого комментария, я думаю, что текст ответа & quot;\s в основном будут соответствовать одним и тем же символам & quot;is wrong: пожалуйста, проверьте самиpreg_replace('/\s/u', '',$s) когда$s = html_entity_decode(" Hello   "); фонтаны UTF8NBSP.
4

неразрывный пробел) в строках UTF-8 с помощьюpreg_replace('/^\p{Z}+|\p{Z}+$/u','',$str);

\s будет соответствовать только & quot; совместимому ascii & quot; космический персонажeven with the u modifier.
но\p{Z} будет соответствовать всем известным символам пространства Юникод

Я редактировал @deceze, смотрите о/\s/uневерно утверждать, что "будет соответствовать только ASCII" (поскольку & amp; # 160; не является ASCII), можете ли вы исправить это в своем ответе? Около\p{Z}Извините, что я не процитировал это в моей редакции, это хорошо запомнить (!).
5

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);
    }
}

Не поддерживает & quot; .. & quot; хотя для диапазонов.

Это не дляmb_trim('000foo000', '0')... :-3
Исправлено это @deceze. Благодарю.
Хорошо поймал! Спасибо.
Мне нравится ваш способ, но не забывайте предварительно цитировать ваш $ charlist :)
Это должно быть немного изменено. Ваша строка $ charlist = preg_quote должна находиться внутри else, иначе проверка is_null ($ charlist) никогда не работает.
0

My two cents

что вы должны сначала выполнить проверки кодировки, прежде чем работать над изменением внешних входных строк. Многие быстро узнают о «дезинфекции и проверке» входные данные, но не спешат изучать шаг определения основной природы (кодировки символов) строк, с которыми они работают на раннем этапе.

Сколько байтов будет использовано для представления каждого символа? При правильно отформатированном UTF-8 это может быть 1 (символыtrim имеет дело с), 2, 3 или 4 байта. Проблема возникает, когда в игру вступают устаревшие или искаженные представления UTF-8 - границы байтовых символов могут не совпадать, как ожидалось (говорят дилетанты).

В PHP некоторые отстаивают необходимость принудительного соответствия всех строк правильной кодировке UTF-8 (1, 2, 3 или 4 байта на символ), где такие функции, какtrim() будет по-прежнему работать, потому что граница байта / символа для символов, с которыми он работает, будет конгруэнтной для значений расширенного ASCII / 1-байта, которыеtrim() стремится исключить начало и конец строки (обрезать страницу руководства).

Однако, поскольку компьютерное программирование является разнообразной областью, невозможно иметь общий подход, который работает во всех сценариях. С учетом вышесказанного, напишите ваше приложение так, как оно должно быть для правильной работы. Просто делаете базовый веб-сайт, управляемый базой данных, с вводом формы?Yes, за мои деньги заставь все быть UTF-8.

Note: У вас все еще будут проблемы с интернационализацией, даже если ваша проблема с UTF-8 стабильна. Зачем? Многие неанглийские наборы символов существуют в 2, 3 или 4-байтовом пространстве (кодовые точки и т. Д.). Очевидно, что если вы используете компьютер, который должен работать со скриптами на китайском, японском, русском, арабском или иврите, вы хотите, чтобы все работало с 2, 3 и 4 байтами! Помните, PHPtrim Функция может обрезать символы по умолчанию или заданные пользователем. Это имеет значение, особенно если вам нужна вашаtrim чтобы объяснить некоторые китайские иероглифы.

Я бы предпочел иметь дело с проблемой невозможности доступа к моему сайту, а затем с проблемой доступа и ответов, которые не должны появляться. Когда вы думаете об этом, это соответствует принципамleast privilege (безопасность) иuniversal design (Доступность).

Summary

Если входные данные не будут соответствовать надлежащей кодировке 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);
}
18

что вы пытаетесь сделать с этой бесконечной рекурсивной функцией, которую вы определяете, но если вам просто нужна многобайтово-безопасная обрезка, это сработает.

function mb_trim($str) {
  return preg_replace("/(^\s+)|(\s+$)/us", "", $str); 
}
Знают ли pregs в PHP о различных кодировках? Я не могу вспомнить, но я знаю, что с ними когда-то была проблема, и я думаю, что это было здесь.
Было бы лучше использовать не захватывающие подпаттеры.us1.php.net/manual/en/regexp.reference.subpatterns.php , У них есть форма(?: )
trim($s,'') а такжеtrim($s,' ') работает отлично (!). Зачем намmb_trim()?
@Incognito Модификатор / u информирует preg о юникоде
5

я принял решение @ edson-medina, исправил ошибку и добавил несколько юнит-тестов. Вот три функции, которые мы используем, чтобы дать аналогам mb параметры trim, 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', ' Å'));
}

Похожие вопросы