Wyobrażam sobie, że muszę usunąć znaki 0-31 i 127,
Czy istnieje funkcja lub fragment kodu, który umożliwia wydajne wykonanie tego zadania.
Wyobrażam sobie, że muszę usunąć znaki 0-31 i 127,
Czy istnieje funkcja lub fragment kodu, który umożliwia wydajne wykonanie tego zadania.
Odpowiedzi:
Jeśli twój Tardis właśnie wylądował w 1963 roku i chcesz tylko 7-bitowych znaków ASCII do druku, możesz wydrzeć wszystko od 0-31 i 127-255 za pomocą tego:
$string = preg_replace('/[\x00-\x1F\x7F-\xFF]/', '', $string);
Dopasowuje wszystko z zakresu 0-31, 127-255 i usuwa to.
Wpadłeś w Hot Tub Time Machine i wróciłeś do lat osiemdziesiątych. Jeśli masz jakąś formę 8-bitowego ASCII, możesz chcieć zachować znaki w zakresie 128-255. Łatwa regulacja - wystarczy spojrzeć na 0-31 i 127
$string = preg_replace('/[\x00-\x1F\x7F]/', '', $string);
Ach, witaj z powrotem w XXI wieku. Jeśli masz ciąg zakodowany w UTF-8, wówczas /u
modyfikator może być użyty w wyrażeniu regularnym
$string = preg_replace('/[\x00-\x1F\x7F]/u', '', $string);
To tylko usuwa 0-31 i 127. Działa to w ASCII i UTF-8, ponieważ oba mają ten sam zakres zestawu kontrolnego (jak zauważył mgutt poniżej). Ściśle mówiąc, działałoby to bez rozszerzenia/u
modyfikatora. Ale to ułatwia życie, jeśli chcesz usunąć inne znaki ...
Jeśli masz do czynienia z Unicode, istnieje potencjalnie wiele elementów niedrukowalnych , ale rozważmy prosty: BEZ PRZERWY SPACE (U + 00A0)
W łańcuchu UTF-8 byłoby to zakodowane jako 0xC2A0
. Możesz poszukać i usunąć tę konkretną sekwencję, ale mając /u
modyfikator na miejscu, możesz po prostu dodać \xA0
do klasy postaci:
$string = preg_replace('/[\x00-\x1F\x7F\xA0]/u', '', $string);
preg_replace jest dość wydajne, ale jeśli wykonujesz tę operację dużo, możesz zbudować tablicę znaków, które chcesz usunąć, i użyć str_replace, jak zauważył mgutt poniżej, np.
//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);
Intuicyjnie wydaje się, że byłoby to szybkie, ale nie zawsze tak jest, zdecydowanie powinieneś przetestować test porównawczy, aby sprawdzić, czy cokolwiek zaoszczędzi. Wykonałem kilka testów porównawczych dla różnych długości ciągów z losowymi danymi i ten wzorzec pojawił się przy użyciu 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
Same czasy dotyczą 10000 iteracji, ale bardziej interesujące są względne różnice. Do 512 znaków, widziałem, że preg_replace zawsze wygrywa. W zakresie 1-8 kb str_replace miał marginalną krawędź.
Pomyślałem, że to ciekawy wynik, więc dołączam go tutaj. Ważne jest, aby nie brać tego wyniku i używać go do decydowania, której metody użyć, ale porównać je z własnymi danymi, a następnie zdecydować.
Wiele z pozostałych odpowiedzi nie uwzględnia znaków Unicode (np. Öäüßйȝîûηы ე மி ᚉ ⠛). W takim przypadku możesz skorzystać z:
$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x9F]/u', '', $string);
W zakresie znajduje się dziwna klasa znaków \x80-\x9F
(tuż nad 7-bitowym zakresem znaków ASCII), które są technicznie znakami kontrolnymi, ale z czasem były nadużywane w przypadku znaków drukowalnych. Jeśli nie masz z nimi żadnych problemów, możesz użyć:
$string = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $string);
Jeśli chcesz również usunąć wysunięcia wiersza, powrót karetki, tabulatory, nierozdzielające spacje i miękkie łączniki, możesz użyć:
$string = preg_replace('/[\x00-\x1F\x7F-\xA0\xAD]/u', '', $string);
Zauważ, że ty w powyższych przykładach musisz używać pojedynczych cudzysłowów.
Jeśli chcesz usunąć wszystko oprócz podstawowych drukowalnych znaków ASCII (wszystkie powyższe przykładowe znaki zostaną usunięte), możesz użyć:
$string = preg_replace( '/[^[:print:]]/', '',$string);
Więcej informacji można znaleźć pod adresem http://www.fileformat.info/info/charset/UTF-8/list.htm
'/[\x00-\x1F\x80-\xC0]/u'
pozostawia je nienaruszone; ale także znak dzielenia (F7) i mnożenia (D7).
\x7F-\x9F
?
Począwszy od PHP 5.2 mamy również dostęp do zmiennej filter_var, o której nie widziałem żadnej wzmianki, więc pomyślałem, że ją tam wyrzucę. Aby użyć zmiennej filter_var do usunięcia niedrukowalnych znaków <32 i> 127, możesz:
Filtruj znaki ASCII poniżej 32
$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
Filtruj znaki ASCII powyżej 127
$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH);
Usuń oba:
$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW|FILTER_FLAG_STRIP_HIGH);
Możesz także zakodować małe znaki w html (nowa linia, tabulacja itp.), Jednocześnie usuwając wysokie:
$string = filter_var($input, FILTER_UNSAFE_RAW, FILTER_FLAG_ENCODE_LOW|FILTER_FLAG_STRIP_HIGH);
Istnieją również opcje usuwania kodu HTML, oczyszczania wiadomości e-mail i adresów URL itp. Tak więc wiele opcji oczyszczania (usuwanie danych), a nawet walidacji (zwraca false, jeśli nie jest poprawne, zamiast cichego usuwania).
Sanityzacja: http://php.net/manual/en/filter.filters.sanitize.php
Walidacja: http://php.net/manual/en/filter.filters.validate.php
Jednak nadal istnieje problem, że FILTER_FLAG_STRIP_LOW usunie znak nowej linii i powrotu karetki, które dla obszaru tekstowego są całkowicie prawidłowymi znakami ... więc niektóre odpowiedzi Regex są, jak sądzę, czasami potrzebne, np. Po przejrzeniu tego wątku, planuję to zrobić dla obszarów tekstowych:
$string = preg_replace( '/[^[:print:]\r\n]/', '',$input);
Wydaje się to bardziej czytelne niż kilka wyrażeń regularnych, które zostały usunięte przez zakres liczbowy.
możesz używać klas postaci
/[[:cntrl:]]+/
to jest prostsze:
$ string = preg_replace ('/ [^ [: cntrl:]] /', '', $ string);
Wszystkie rozwiązania działają częściowo, a nawet poniżej prawdopodobnie nie obejmuje wszystkich przypadków. Mój problem polegał na próbie wstawienia ciągu do tabeli utf8 mysql. Łańcuch (i jego bajty) był zgodny z utf8, ale miał kilka złych sekwencji. Zakładam, że większość z nich była kontrolna lub formatująca.
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;
}
Aby jeszcze bardziej zaostrzyć problem, tabela, serwer, połączenie, renderowanie treści, o czym trochę tutaj mówiliśmy
$s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);
ponieważ wszystkie postacie emoji psowały mysql
Możesz użyć wyrażenia regularnego, aby usunąć wszystko oprócz tych znaków, które chcesz zachować:
$string=preg_replace('/[^A-Za-z0-9 _\-\+\&]/','',$string);
Zastępuje wszystko, co nie jest (^) literami AZ lub az, cyframi 0-9, spacją, podkreśleniem, łącznikiem, plusem i ampersandem - niczym (tzn. Usuń to).
preg_replace('/(?!\n)[\p{Cc}]/', '', $response);
Spowoduje to usunięcie wszystkich znaków kontrolnych ( http://uk.php.net/manual/en/regexp.reference.unicode.php ), pozostawiając \n
znaki nowej linii. Z mojego doświadczenia wynika, że znaki sterujące są tymi, które najczęściej powodują problemy z drukowaniem.
/u
dla znaków UTF-8. Czy mógłbyś wyjaśnić, co (?!\n)
robi pierwsza część ?
Aby usunąć wszystkie znaki spoza zestawu ASCII z ciągu wejściowego
$result = preg_replace('/[\x00-\x1F\x80-\xFF]/', '', $string);
Ten kod usuwa wszystkie znaki z zakresów szesnastkowych 0-31 i 128-255, pozostawiając tylko znaki szesnastkowe 32-127 w ciągu wynikowym, który w tym przykładzie nazywam $ result.
Odpowiedź @PaulDixon jest całkowicie błędne , ponieważ usuwa druku rozszerzonych znaków ASCII 128-255!został częściowo poprawiony. Nie wiem, dlaczego nadal chce usunąć 128-255 z zestawu 127 znaków 7-bitowego ASCII, ponieważ nie ma on rozszerzonych znaków ASCII.
Ale ostatecznie ważne było, aby nie usuwać 128-255, ponieważ na przykład chr(128)
( \x80
) to znak euro w 8-bitowym ASCII, a wiele czcionek UTF-8 w systemie Windows wyświetla znak euro i Androida dotyczący mojego własnego testu.
I zabije wiele znaków UTF-8, jeśli usuniesz znaki ASCII 128-255 z łańcucha UTF-8 (prawdopodobnie początkowe bajty wielobajtowego znaku UTF-8). Więc nie rób tego! Są to całkowicie legalne postacie we wszystkich obecnie używanych systemach plików. Jedyny zarezerwowany zakres to 0-31 .
Zamiast tego użyj tego, aby usunąć niedrukowalne znaki 0-31 i 127:
$string = preg_replace('/[\x00-\x1F\x7F]/', '', $string);
To działa w ASCII i UTF-8 , ponieważ zarówno akcję samym przedziale zestaw sterujący .
Najszybciej slower¹ alternatywa bez użycia wyrażeń regularnych:
$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);
Jeśli chcesz zachować wszystkie białe znaki \t
, \n
i \r
, a następnie usunąć chr(9)
, chr(10)
i chr(13)
z tej listy. Uwaga: Zwykła biała spacja chr(32)
pozostaje w wyniku. Zdecyduj sam, czy chcesz usunąć niepękającą przestrzeń, chr(160)
ponieważ może to powodować problemy.
¹ Przetestowane przez @PaulDixon i zweryfikowane przeze mnie.
Oznaczony anwser jest doskonały, ale pomija znak 127 (DEL), który jest również znakiem niedrukowalnym
moja odpowiedź brzmiałaby
$string = preg_replace('/[\x00-\x1F\x7f-\xFF]/', '', $string);
"cedivad" rozwiązał dla mnie problem z trwałym wynikiem szwedzkich znaków ÅĘÖ.
$text = preg_replace( '/[^\p{L}\s]/u', '', $text );
Dzięki!
Dla każdego, kto wciąż szuka, jak to zrobić bez usuwania niedrukowalnych znaków, ale raczej przed nimi, zrobiłem to, aby pomóc. Zapraszam do ulepszania! Znaki są zapisywane do \\ x [A-F0-9] [A-F0-9].
Zadzwoń tak:
$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));
}
?>
Rozwiązałem problem dla UTF8 używając https://github.com/neitanod/forceutf8
use ForceUTF8\Encoding;
$string = Encoding::fixUTF8($string);
Wyrażenie regularne do wybranej odpowiedzi nie działa dla Unicode: 0x1d (z php 7.4)
rozwiązanie:
<?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 Łańcuch usuwa wszystkie niewidoczne znaki z wyjątkiem nowej linii