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

161

Я думаю, мне нужно удалить символы 0-31 и 127,

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

Стюарт Робинсон
источник

Ответы:

355

7 бит ASCII?

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

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

Он соответствует чему-либо в диапазоне 0-31, 127-255 и удаляет его.

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

Вы попали в машину времени с джакузи и вернулись в восьмидесятые. Если у вас есть какая-то форма 8-битного ASCII, то вы можете оставить символы в диапазоне 128-255. Простая настройка - просто посмотрите на 0-31 и 127

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

UTF-8?

Ах, добро пожаловать обратно в 21 век. Если у вас есть строка в кодировке UTF-8, то /u модификатор можно использовать в регулярном выражении

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

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

Если вы имеете дело с Юникодом, потенциально существует много непечатаемых элементов , но давайте рассмотрим простой: 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, как указано в mgutt ниже, например

//build an array we can re-use across several operations
$badchar=array(
    // control characters
    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),
    // non-printing characters
    chr(127)
);

//replace the unwanted chars
$str2 = str_replace($badchar, '', $str);

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

     2 chars str_replace     5.3439ms preg_replace     2.9919ms preg_replace is 44.01% faster
     4 chars str_replace     6.0701ms preg_replace     1.4119ms preg_replace is 76.74% faster
     8 chars str_replace     5.8119ms preg_replace     2.0721ms preg_replace is 64.35% faster
    16 chars str_replace     6.0401ms preg_replace     2.1980ms preg_replace is 63.61% faster
    32 chars str_replace     6.0320ms preg_replace     2.6770ms preg_replace is 55.62% faster
    64 chars str_replace     7.4198ms preg_replace     4.4160ms preg_replace is 40.48% faster
   128 chars str_replace    12.7239ms preg_replace     7.5412ms preg_replace is 40.73% faster
   256 chars str_replace    19.8820ms preg_replace    17.1330ms preg_replace is 13.83% faster
   512 chars str_replace    34.3399ms preg_replace    34.0221ms preg_replace is  0.93% faster
  1024 chars str_replace    57.1141ms preg_replace    67.0300ms str_replace  is 14.79% faster
  2048 chars str_replace    94.7111ms preg_replace   123.3189ms str_replace  is 23.20% faster
  4096 chars str_replace   227.7029ms preg_replace   258.3771ms str_replace  is 11.87% faster
  8192 chars str_replace   506.3410ms preg_replace   555.6269ms str_replace  is  8.87% faster
 16384 chars str_replace  1116.8811ms preg_replace  1098.0589ms preg_replace is  1.69% faster
 32768 chars str_replace  2299.3128ms preg_replace  2222.8632ms preg_replace is  3.32% faster

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

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

Пол Диксон
источник
14
Если вам нужно считать символ новой строки безопасным, измените выражение на это (обратный поиск печатных форм): preg_replace (/ [^ \ x0A \ x20- \ x7E] /, '', $ string);
Ник
12
@Dalin Нет такой вещи как «символ UTF-8». Есть символы / символы Unicode, и UTF-8 - это кодировка, которая может представлять все из них. Вы хотели сказать, что это не работает для символов вне набора символов ASCII.
Матиас Биненс
3
Если вам нужно сопоставить символ Юникода выше \ xFF, используйте \ x {####}
Питер Олсон
Вы пропустили \ x7F (127), который не
печатается
это удалит арабские буквы, плохое решение.
Айман Хуссейн
141

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

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

Для справки см. Http://www.fileformat.info/info/charset/UTF-8/list.htm.

Далин
источник
1
Ваше регулярное выражение прекрасно обрабатывает символы UTF8; но он удаляет «специальные» символы не-UTF8; как ç, ü и ö. '/[\x00-\x1F\x80-\xC0]/u'оставляет их нетронутыми; но также знак деления (F7) и умножения (D7).
Хазар
@ Хацар, да, вы правы, \ x80- \ xFF слишком много раздели, но \ x80- \ xC0 все еще слишком ограничен. Это будет пропускать другие печатные символы, такие как © £ ±. Для справки смотрите utf8-chartable.de
Dalin
1
@TimMalone, потому что PHP расширит эти последовательности символов: php.net/manual/en/… так что регулярное выражение не увидит диапазон, о котором вы пытаетесь рассказать.
Далин
1
Что насчет 7F? Не должно ли это быть \x7F-\x9F?
Звонок
1
Я просто много пробовал, я пробовал все функции кодирования, доступные в PHP, от regex до mb_ до htmlspecialchars и т. Д. Ничто не удаляло управляющие символы, спасибо за вложение работы.
Джон
29

Начиная с 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 будет отбрасывать символы новой строки и возврата каретки, которые для текстовой области являются полностью допустимыми символами ... поэтому некоторые из ответов Regex, я думаю, иногда все же необходимы, например, после просмотра этого я планирую сделать это для textareas:

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

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

Кевин Нельсон
источник
27

Вы можете использовать классы персонажей

/[[:cntrl:]]+/
ghostdog74
источник
разве это не требует от меня использования ereg?
Стюарт Робинсон
18

это проще:

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

jacktrade
источник
5
Это также удаляет перевод строки, возврат каретки и символы UTF8.
Далин
5
@Dalin Нет такой вещи как «символ UTF-8». Есть символы / символы Unicode, и UTF-8 - это кодировка, которая может представлять все из них. Вы хотели сказать, что это также удаляет символы вне диапазона ASCII .
Матиас Биненс
1
Кушает арабские символы :)
Рольф
16

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

function clean_string($string) {
  $s = trim($string);
  $s = iconv("UTF-8", "UTF-8//IGNORE", $s); // drop all non utf-8 characters

  // this is some bad utf-8 byte sequence that makes mysql complain - control and formatting i think
  $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); // reduce all multiple whitespace to a single space

  return $s;
}

Чтобы еще больше усугубить проблему, это таблица "сервер против сервера", "соединение" и "рендеринг контента", о чем немного говорилось здесь.

Уэйн Вейбел
источник
1
Единственный, который проходит все мои юнит-тесты, круто!
Корри
\ xE2 \ x80 [\ xA4- \ xA8] (или 226.128. [164-168]) - неверно, последовательность включает следующие печатаемые символы: символ Unicode 'ONE DOT LEADER' (U + 2024), символ Unicode 'TWO DOT ЛИДЕР '(U + 2025), Unicode-символ' ГОРИЗОНТАЛЬНЫЙ ЭЛЛИПСИС '(U + 2026), Unicode-символ' HYPHENATION POINT '(U + 2027). И только один непечатаемый: Unicode Character 'LINE SEPARATOR' (U + 2028). Следующий также не предназначен для печати: Unicode-символ 'PARAGRAPH SEPARATOR' (U + 2029). Поэтому замените последовательность на: \ xE2 \ x80 [\ xA8- \ xA9] \ xE2 \ x80 [\ xA8- \ xA9], чтобы удалить разделитель строк и разделитель параграфов.
MingalevME
Это лучшее решение, которое я мог найти до сих пор, но я должен был добавить, $s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);потому что все персонажи смайликов
Джо Блэк
10

Моя версия, совместимая с UTF-8:

preg_replace('/[^\p{L}\s]/u','',$value);

cedivad
источник
7
Это удаляет такие символы, как кавычки, скобки и т. Д. Это, безусловно, печатные символы.
Gajus
это замечательно! это спасло мне жизнь, испортило при печати арабские символы, работало как чемпион :)
Кришна
6

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

$string=preg_replace('/[^A-Za-z0-9 _\-\+\&]/','',$string);

Заменяет все, что не является (^) буквами AZ или az, числами 0-9, пробелом, подчеркиванием, гипеном, плюсом и амперсандом - ничем (т.е. удаляет его).

Ричи Б.
источник
5
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);

Это удалит все управляющие символы ( http://uk.php.net/manual/en/regexp.reference.unicode.php ) выдерживание \nсимволы новой строки. Исходя из моего опыта, именно контрольные символы чаще всего вызывают проблемы с печатью.

Gajus
источник
1
Это прекрасно работает для меня! Я добавил только /uдля UTF-8 символов. Не могли бы вы объяснить, что (?!\n)делает первая часть ?
Марсио Маццукато,
4

Для удаления всех не-ASCII символов из входной строки

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

Этот код удаляет все символы в шестнадцатеричных диапазонах 0-31 и 128-255, оставляя только шестнадцатеричные символы 32-127 в результирующей строке, которую я называю $ result в этом примере.

Джунаид Масуд
источник
3

Ответ @PaulDixon это совершенно неправильно , потому что она удаляет печатаемые расширенные символы ASCII 128-255! был частично исправлен. Я не знаю, почему он все еще хочет удалить 128-255 из 127-битного набора ASCII из 127 символов, поскольку он не имеет расширенных символов ASCII.

Но, наконец, важно не удалять 128-255, потому что, например, chr(128)( \x80) - это знак евро в 8-битном ASCII, и многие шрифты UTF-8 в Windows отображают знак евро и Android относительно моего собственного теста.

И он убьет много символов UTF-8, если вы удалите символы ASCII 128-255 из строки UTF-8 (вероятно, начальные байты многобайтового символа UTF-8). Так что не делай этого! Они являются полностью допустимыми символами во всех используемых в настоящее время файловых системах. Единственный зарезервированный диапазон - 0-31 .

Вместо этого используйте это, чтобы удалить непечатаемые символы 0-31 и 127:

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

Он работает в ASCII и UTF-8, потому что оба используют один и тот же диапазон набора управления .

Самая быстрая медленная альтернатива без использования регулярных выражений:

$string = str_replace(array(
    // control characters
    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),
    // non-printing characters
    chr(127)
), '', $string);

Если вы хотите сохранить все символы пробелов \t, \nи \r, затем удалить chr(9), chr(10)и chr(13)из этого списка. Примечание. Обычный пробел - chr(32)это то, что остается в результате. Решите сами, хотите ли вы удалить неразрывный пробел, так chr(160)как это может вызвать проблемы.

¹ Проверено @PaulDixon и проверено мной.

mgutt
источник
2

как насчет:

return preg_replace("/[^a-zA-Z0-9`_.,;@#%~'\"\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:\-\s\\\\]+/", "", $data);

дает мне полный контроль над тем, что я хочу включить

sdfor
источник
0

Помеченный anwser идеален, но ему не хватает символа 127 (DEL), который также не предназначен для печати

мой ответ будет

$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
Mubashar
источник
Этот ответ тоже неверен. См .: stackoverflow.com/a/42058165/318765
mgutt
ответ выше был комплиментом к оригинальному ответу, который только добавляет символ «удалить».
Мубашар
0

«Седивад» решил проблему для меня с постоянным результатом шведских символов ÅÄÖ.

$text = preg_replace( '/[^\p{L}\s]/u', '', $text );

Спасибо!

Андреас Эк
источник
0

Для тех, кто все еще ищет, как это сделать, не удаляя непечатаемые символы, а скорее избегая их, я сделал это, чтобы помочь. Не стесняйтесь улучшать это! Символы экранируются в \\ x [A-F0-9] [A-F0-9].

Звоните так:

$escaped = EscapeNonASCII($string);

$unescaped = UnescapeNonASCII($string);

<?php 
  function EscapeNonASCII($string) //Convert string to hex, replace non-printable chars with escaped hex
    {
        $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]); //Convert top nibble to hex
        $bottomnibble = bin2hex($string[1]); //Convert bottom nibble to hex
        $escaped = $x . $topnibble . $bottomnibble; //Concatenate escape sequence "\x" with top and bottom nibble
        return $escaped;
    }

    function UnescapeNonASCII($string) //Convert string to hex, replace escaped hex with actual hex.
    {
        $stringtohex = bin2hex($string);
        $stringtohex = preg_replace_callback('/5c5c78([a-fA-F0-9]{4})/', function ($m) { 
            return hex2bin($m[1]);
        }, $stringtohex);
        return hex2bin(strtoupper($stringtohex));
    }
?>
DropItLikeItsHot
источник
0

Я решил проблему для UTF8, используя https://github.com/neitanod/forceutf8

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);
МНВ
источник
1
Эта библиотека преобразует символы UTF-8 с акцентом и смайлики UTF-8 в "?" символы. Довольно серьезная проблема, к сожалению.
ChristoKiwi
0

Регулярное выражение в выбранный ответ не работает для Unicode: 0x1d (с php 7.4)

решение:

<?php
        $ct = 'différents'."\r\n test";

        // fail for Unicode: 0x1d
        $ct = preg_replace('/[\x00-\x1F\x7F]$/u', '',$ct);

        // work for Unicode: 0x1d
        $ct =  preg_replace( '/[^\P{C}]+/u', "",  $ct);

        // work for Unicode: 0x1d and allow line break
        $ct =  preg_replace( '/[^\P{C}\n]+/u', "",  $ct);

        echo $ct;

from: UTF 8 String удалить все невидимые символы, кроме новой строки

Mkdgs
источник