Web

Как удалить все непечатаемые символы в строке

Я предполагаю, мне нужно удалить символы с кодами «0-31» и «127».

Есть ли функция или фрагмент кода, чтобы сделать это эффективно?

 

 Ответ 1

7-битный ASCII?

Если вы используете устаревшие системы и вам просто нужны 7-битные печатные символы ASCII, вы можете исключить все символы из диапазонов «0-31» и «127-255» с помощью этого:

$string = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $string);

8-битный расширенный ascii?

Если у вас есть какая-то форма 8-битного ASCII, вы можете оставить символы в диапазоне «128-255».

$string = preg_replace('/[\x00-\x1F\x7F]/', '', $string);

UTF-8?

Если у вас есть строка в кодировке UTF-8, то модификатор «/u» можно использовать в регулярном выражении следующим образом:

$string = preg_replace('/[\x00-\x1F\x7F]/u', '', $string);

 Этот код просто удаляет символы из диапазона «0-31» и символ «127». Это работает в ASCII и UTF-8, потому что оба используют один и тот же диапазон набора элементов управления (как указано ниже). Строго говоря, это сработало бы и без модификатора «/u». Но лучше «перестраховаться», если вы хотите удалить символы из других наборов юникода...

Если вы имеете дело с Unicode, существует много потенциально непечатных символов, но давайте рассмотрим простой вариант: NO-BREAK SPACE (U + 00A0).

В строке UTF-8 это будет закодировано как 0xC2A0. Вы можете найти и удалить эту конкретную последовательность, но с установленным модификатором «/u» вы можете просто добавить \xA0 к классу символов:

$string = preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $string);

Приложение: использование str_replace?

preg_replace довольно эффективен, но, если вы выполняете эту операцию много раз, вы можете создать массив символов, которые хотите удалить, и использовать str_replace, как указано ниже, например:

//создать массив, который мы можем повторно использовать в нескольких операциях

$badchar=array(

    // управляющие символы

    chr(0), chr(1), chr(2), chr(3), chr(4), chr(5), chr(6), chr(7), chr(8), chr(9), chr(10),

    chr(11), chr(12), chr(13), chr(14), chr(15), chr(16), chr(17), chr(18), chr(19), chr(20),

    chr(21), chr(22), chr(23), chr(24), chr(25), chr(26), chr(27), chr(28), chr(29), chr(30),

    chr(31),

    // непечатные символы

    chr(127)

);

//замените ненужные символы

$str2 = str_replace($badchar, '', $str);

 Интуитивно кажется, что это будет работать быстро, но это не всегда так; вам обязательно нужно выполнить тест, чтобы увидеть, действительно ли это так. Я провел несколько тестов для различных длин строк со случайными данными, и этот шаблон появился с использованием php 7.0.12.

     2 chars str_replace     5.3439ms preg_replace     2.9919ms preg_replace на 44.01% быстрее

     4 chars str_replace     6.0701ms preg_replace     1.4119ms preg_replace на 76.74% быстрее

     8 chars str_replace     5.8119ms preg_replace     2.0721ms preg_replace на 64.35% быстрее

    16 chars str_replace     6.0401ms preg_replace     2.1980ms preg_replace на 63.61% быстрее

    32 chars str_replace     6.0320ms preg_replace     2.6770ms preg_replace на 55.62% быстрее

    64 chars str_replace     7.4198ms preg_replace     4.4160ms preg_replace на 40.48% быстрее

   128 chars str_replace    12.7239ms preg_replace     7.5412ms preg_replace на 40.73% быстрее

   256 chars str_replace    19.8820ms preg_replace    17.1330ms preg_replace на 13.83% быстрее

   512 chars str_replace    34.3399ms preg_replace    34.0221ms preg_replace на  0.93% быстрее

  1024 chars str_replace    57.1141ms preg_replace    67.0300ms str_replace  на 14.79% быстрее

  2048 chars str_replace    94.7111ms preg_replace   123.3189ms str_replace  на 23.20% быстрее

  4096 chars str_replace   227.7029ms preg_replace   258.3771ms str_replace  на 11.87% быстрее

  8192 chars str_replace   506.3410ms preg_replace   555.6269ms str_replace  на  8.87% быстрее

 16384 chars str_replace  1116.8811ms preg_replace  1098.0589ms preg_replace на  1.69% быстрее

 32768 chars str_replace  2299.3128ms preg_replace  2222.8632ms preg_replace на  3.32% быстрее

 Сами тайминги рассчитаны на 10 000 итераций, но более интересны относительные различия. До 512 символов я видел, что preg_replace всегда выигрывает. В диапазоне 1-8 кб у str_replace было незначительное преимущество.

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

 

Ответ 2

Предыдущий ответ не учитывает символы юникода (например, «öäüßйȝîûηыமிᚉ⠛»). В этом случае вы можете использовать следующее:

$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u', '', $string);

 Существует неоднозначный класс символов в диапазоне \x80-\x9F (чуть выше 7-битного диапазона символов ASCII), которые технически являются управляющими символами, но со временем их стали неправильно использовать в качестве печатаемых символов. Если у вас нет проблем с ними, то вы можете использовать:

$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $string);

 Если вы хотите также удалить перевод строки, возврат каретки, табуляцию, неразрывные пробелы и «мягкий» дефис, вы можете использовать:

$string = preg_replace('/[\x00-\x1F\x7F-\xA0\xAD]/u', '', $string);

 Обратите внимание, что в приведенных выше примерах необходимо использовать одинарные кавычки. 

Если вы хотите удалить все, кроме основных печатаемых символов ASCII (все приведенные выше символы будут удалены), вы можете использовать:

$string = preg_replace( '/[^[:print:]]/', '',$string);

 

Ответ 3

Начиная с PHP 5.2, у нас также есть доступ к filter_var, о котором я не встречал упоминаний, поэтому решил рассказать о нем. Чтобы использовать filter_var для удаления непечатаемых символов < 32 и > 127, вы можете сделать следующее:

Фильтр ASCII символов ниже 32

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);

 Фильтр ASCII символов выше 127

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH);

Конечный вариант:

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH);

Вы также можете html-кодировать символы (новая строка, табуляция и т. д.):

$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_ENCODE_LOW|FILTER_FLAG_STRIP_HIGH);

Есть также опции для удаления HTML, проверки электронных писем и URL-адресов и т. д. Итак, множество вариантов для санирования (удаление данных) и даже валидации (возврат false в случае недействительности вместо простого удаления).

Санирование: http://php.net/manual/en/filter.filters.sanitize.php

Валидация: http://php.net/manual/en/filter.filters.validate.php

Однако все еще существует проблема, так как  FILTER_FLAG_STRIP_LOW будет удалять новую строку и возврат каретки, которые для текстовой области являются полностью допустимыми символами... поэтому некоторые из результатов регекспов все еще допустимы:

$string = preg_replace( '/[^[:print:]\r\n]/', '',$input);

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


Ответ 4

Все решения работают частично, и даже приведенные ниже, вероятно, не охватывают всех случаев. Моя проблема заключалась в попытке вставить строку в таблицу mysql с utf8. Строка (и ее байты) соответствовала utf8, но имелось несколько неудачных последовательностей. Я предполагаю, что большинство из них были управляющими или форматирующими.

function clean_string($string) {

  $s = trim($string);

  $s = iconv("UTF-8", "UTF-8//IGNORE", $s); // отбрасываем все символы, отличные от utf-8

  // это какая-то недопустимая последовательность байтов utf-8, которая заставляет mysql выдавать ошибку

  $s = preg_replace('/(?>[\x00-\x1F]|\xC2[\x80-\x9F]|\xE2[\x80-\x8F]{2}|\xE2\x80[\xA4-\xA8]|\xE2\x81[\x9F-\xAF])/', ' ', $s);

  $s = preg_replace('/\s+/', ' ', $s); // сократите все множественные пробелы до одного пробела

  return $s;

}

 

Ответ 5

<?php 

  function EscapeNonASCII($string)  {

       //Преобразовать строку в шестнадцатеричный формат, заменить непечатаемые символы на шестнадцатеричные.

        $hexbytes = strtoupper(bin2hex($string));

        $i = 0;

        while ($i < strlen($hexbytes)) {

            $hexpair = substr($hexbytes, $i, 2);

            $decimal = hexdec($hexpair);

            if ($decimal < 32 || $decimal > 126) {

                $top = substr($hexbytes, 0, $i);

                $escaped = EscapeHex($hexpair);

                $bottom = substr($hexbytes, $i + 2);

                $hexbytes = $top . $escaped . $bottom;

                $i += 8;

            }

            $i += 2;

        }

        $string = hex2bin($hexbytes);

        return $string;

    }

    function EscapeHex($string) //Helper function for EscapeNonASCII() {

        $x = "5C5C78"; //\x

        $topnibble = bin2hex($string[0]); //Преобразование старшего полубайта в шестнадцатеричный формат

        $bottomnibble = bin2hex($string[1]); //Перевести нижний полубайт в шестнадцатеричный формат

        $escaped = $x . $topnibble . $bottomnibble; //Конкатенация управляющей последовательности "\x" с верхним и нижним полубайтом

        return $escaped;

    }

    function UnescapeNonASCII($string) {

        //Преобразование строки в шестнадцатеричный формат, замена приведенного шестнадцатеричного формата на фактический.

        $stringtohex = bin2hex($string);

        $stringtohex = preg_replace_callback('/5c5c78([a-fA-F0-9]{4})/', function ($m) { 

            return hex2bin($m[1]);

        }, $stringtohex);

        return hex2bin(strtoupper($stringtohex));

    }

?>

 

Схожие статьи

Уязвимости Вордпресс. Чем они грозят владельцу сайта и как исправить
Web

Уязвимости Вордпресс. Чем они грозят владельцу сайта и как исправить

Web

Удаление из массива PHP по значению (не по ключу)

Web

Потеря PHP сеанса после перенаправления

Web

Как получить миниатюру видеоролика YouTube используя YouTube API?