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модифікатора. Але це полегшує життя, якщо ви хочете видалити інші символи ...

Якщо ви маєте справу з 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, як зазначено нижче 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 завжди вигравав. У діапазоні 1-8kb, str_replace мав граничне ребро.

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


14
Якщо вам потрібно вважати новий рядок безпечним, змініть вираз на це (зворотний пошук для друку): preg_replace (/ [^ \ x0A \ x20- \ x7E] /, '', $ string);
Нік

12
@Dalin Не існує такого поняття, як "символ UTF-8". Є символи / символи Unicode, а UTF-8 - це кодування, яке може представляти їх усі. Ви хотіли сказати, що це не працює для символів поза набором символів ASCII.
Матіас Байненс

3
Якщо вам потрібно відповідати символу Unicode вище \ xFF, використовуйте \ x {####}
Пітер Олсон

ви пропустили \ x7F (127), який є
недрукувальним

це видалить арабські літери, неправильне рішення.
Айман Хусейн

141

У багатьох інших відповідях тут не враховуються символи Unicode (наприклад, öäüßйȝîûηы ე மி ᚉ ⠛). У цьому випадку ви можете використовувати наступне:

$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
Ваш regexp добре обробляє символи UTF8; але він знімає "спеціальні" символи, що не належать до UTF8; як ç, ü і ö. '/[\x00-\x1F\x80-\xC0]/u'залишає їх недоторканими; але також знак ділення (F7) та множення (D7).
Хазар

@ Хазар, так, ви правильні \ x80- \ xFF позбавлені занадто багато, але \ x80- \ xC0 все ще занадто обмежуючий. Це не вистачить інших символів для друку, таких як © £ ±. Для довідки див. Utf8-chartable.de
Далін

1
@TimMalone, оскільки PHP розширить ці послідовності символів: php.net/manual/en/…, тож регулярний вираз не побачить діапазон, про який ви намагаєтесь розповісти.
Далін

1
А як щодо 7F? Чи не повинно бути \x7F-\x9F?
Белл

1
Я просто спробував багато, я спробував кожну функцію кодування, доступну в PHP, від регулярного виразу до 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-адрес тощо. Отже, безліч варіантів санітарії (викресліть дані) та навіть перевірки (поверніть помилкове, якщо не дійсне, а не мовчки знімати).

Санітація: 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);

Це здається більш читабельним, ніж кількість регулярних виразів, які викреслюються за числовим діапазоном.



18

це простіше:

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


5
Це також знімає канали рядків, повернення каретки та символи UTF8.
Далін

5
@Dalin Не існує такого поняття, як "символ UTF-8". Є символи / символи Unicode, а UTF-8 - це кодування, яке може представляти їх усі. Ви мали намір сказати це символи смужок і поза діапазоном ASCII .
Mathias Bynens

1
З'їдає арабські символи :)
Рольф

16

Усі рішення працюють частково, а навіть нижче, ймовірно, не охоплює всіх випадків. Моя проблема полягала в спробі вставити рядок у таблицю utf8 mysql. Рядок (і його байти) відповідали 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' HORIZONTAL ELLIPSIS '(U + 2026), символ Unicode' TOUP GYPHENATION '(U + 2027). І лише один недрукувальний символ: Unicode символ "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);тому що всі персонажі смайлів
Джо Блек

9

Моя версія, сумісна UTF-8:

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


7
Це добре видаляє символи, такі як лапки, дужки тощо. Це, безумовно, символи для друку.
Гаджус

це чудово! це врятувало мені життя, заплуталося під час друку арабських символів, працювало як чемпіон :)
krishna

6

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

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

Замінює все, що не є (^) літерами AZ або az, цифрами 0-9, пробіл, підкреслення, hypen, плюс і амперсанд - нічим (тобто видаліть його).


5
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);

Це видалить усі символи керування ( http://uk.php.net/manual/en/regexp.reference.unicode.php ), залишивши \nсимволи нового рядка. З мого досвіду, контрольні символи - це ті, які найчастіше викликають проблеми друку.


1
Це прекрасно працює для мене! Я додав лише /uдля символів UTF-8. Не могли б ви пояснити, що (?!\n)робить перша частина ?
Marcio Mazzucato

4

Щоб зняти всі символи, що не належать до ASCII, з рядка введення

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

Цей код видаляє будь-які символи в шістнадцятковій діапазоні 0-31 та 128-255, залишаючи в отриманій рядку лише шістнадцяткові символи 32-127, які я називаю в цьому прикладі $.


3

Відповідь @PaulDixon це абсолютно неправильно , тому що вона видаляє друкуються розширені символи ASCII 128-255!була частково виправлена. Я не знаю, чому він все ще хоче видалити 128-255 з 127 символів 7-бітного набору ASCII, оскільки він не має розширених символів 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, оскільки обидва мають однаковий діапазон набору керування .

Швидкий slower¹ альтернатива без використання регулярних виразів:

$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 та перевірена власноруч.


2

як щодо:

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

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


0

Позначений анвесер ідеальний, але йому не вистачає символу 127 (DEL), який також є символом, який не можна роздрукувати

моя відповідь буде

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

Ця відповідь також неправильна. Див: stackoverflow.com/a/42058165/318765
mgutt

вищевказана відповідь була компліментом до оригінальної відповіді, яка лише додає символ "видалити".
Мубашар

0

"cedivad" вирішив для мене це питання наполегливим результатом шведських знаків ÅÄÖ.

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

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;

з: UTF 8 String видаліть усі невидимі символи, крім нового рядка

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.