PHP: Làm thế nào để loại bỏ tất cả các ký tự không in được trong một chuỗi?


158

Tôi tưởng tượng tôi cần xóa ký tự 0-31 và 127,

Có một chức năng hoặc đoạn mã để làm điều này một cách hiệu quả.

Câu trả lời:


353

ASCII 7 bit?

Nếu Tardis của bạn vừa hạ cánh vào năm 1963 và bạn chỉ muốn các ký tự ASCII có thể in 7 bit, bạn có thể trích xuất mọi thứ từ 0-31 và 127-255 bằng cách này:

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

Nó phù hợp với bất cứ điều gì trong phạm vi 0-31, 127-255 và loại bỏ nó.

ASCII mở rộng 8 bit?

Bạn rơi vào cỗ máy thời gian bồn tắm nóng, và bạn trở lại những năm tám mươi. Nếu bạn đã có một số dạng ASCII 8 bit, thì bạn có thể muốn giữ các ký tự trong phạm vi 128-255. Một điều chỉnh dễ dàng - chỉ cần tìm 0-31 và 127

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

UTF-8?

Ah, chào mừng trở lại thế kỷ 21. Nếu bạn có chuỗi được mã hóa UTF-8, thì công cụ /u sửa đổi có thể được sử dụng trên regex

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

Điều này chỉ loại bỏ 0-31 và 127. Điều này hoạt động trong ASCII và UTF-8 vì cả hai đều có chung phạm vi điều khiển (như được ghi chú bởi mgutt bên dưới). Nói đúng ra, cái này sẽ hoạt động mà không cần /usửa đổi. Nhưng nó làm cho cuộc sống dễ dàng hơn nếu bạn muốn loại bỏ các ký tự khác ...

Nếu bạn đang xử lý Unicode, có khả năng có nhiều yếu tố không in , nhưng hãy xem xét một yếu tố đơn giản: KHÔNG GIAN KHÔNG GIỐNG (U + 00A0)

Trong chuỗi UTF-8, điều này sẽ được mã hóa thành 0xC2A0. Bạn có thể tìm và loại bỏ chuỗi cụ thể đó, nhưng với công cụ /usửa đổi tại chỗ, bạn chỉ cần thêm \xA0vào lớp ký tự:

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

Phụ lục: Điều gì về str numplace?

preg numplace khá hiệu quả, nhưng nếu bạn thực hiện thao tác này nhiều, bạn có thể tạo một mảng các ký tự bạn muốn xóa và sử dụng str numplace như được ghi chú bởi mgutt bên dưới, ví dụ:

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

Theo trực giác, điều này có vẻ như sẽ nhanh, nhưng không phải lúc nào cũng vậy, bạn chắc chắn nên điểm chuẩn để xem nó có giúp bạn tiết kiệm được gì không. Tôi đã thực hiện một số điểm chuẩn qua nhiều độ dài chuỗi khác nhau với dữ liệu ngẫu nhiên và mẫu này đã xuất hiện bằng cách sử dụng 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

Bản thân thời gian là 10000 lần lặp, nhưng điều thú vị hơn là sự khác biệt tương đối. Lên đến 512 ký tự, tôi đã thấy preg numplace luôn giành chiến thắng. Trong phạm vi 1-8kb, str numplace có lợi thế biên.

Tôi nghĩ rằng đó là kết quả thú vị, vì vậy bao gồm nó ở đây. Điều quan trọng không phải là lấy kết quả này và sử dụng nó để quyết định sử dụng phương pháp nào, mà là điểm chuẩn so với dữ liệu của chính bạn và sau đó quyết định.


14
Nếu bạn cần xem xét một dòng mới an toàn, hãy thay đổi biểu thức thành dòng này (tìm kiếm ngược lại các bản in): preg numplace (/ [^ \ x0A \ x20- \ x7E] /, '', $ string);
Nick

12
@Dalin Không có thứ gọi là ký tự UTF-8. Có các ký hiệu / ký tự Unicode và UTF-8 là một mã hóa có thể đại diện cho tất cả chúng. Bạn muốn nói điều này không hoạt động đối với các ký tự bên ngoài bộ ký tự ASCII.
Mathias Bynens

3
Nếu bạn cần khớp một ký tự unicode ở trên \ xFF, hãy sử dụng \ x {####}
Peter Olson

bạn đã bỏ lỡ \ x7F (127), một ký tự không thể in được
Mubashar

Điều này sẽ loại bỏ các chữ cái Ả Rập, giải pháp xấu.
Ayman Hussein

141

Nhiều câu trả lời khác ở đây không tính đến các ký tự unicode (ví dụ: öäüßйȝîûηы ე). Trong trường hợp này, bạn có thể sử dụng như sau:

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

Có một lớp nhân vật kỳ lạ trong phạm vi \x80-\x9F (Chỉ trên phạm vi ký tự ASCII 7 bit) là các ký tự điều khiển kỹ thuật, nhưng theo thời gian đã bị sử dụng sai cho các ký tự có thể in được. Nếu bạn không có bất kỳ vấn đề nào với những thứ này, thì bạn có thể sử dụng:

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

Nếu bạn cũng muốn loại bỏ nguồn cấp dữ liệu, trả về vận chuyển, tab, không gian không phá vỡ và dấu gạch nối mềm, bạn có thể sử dụng:

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

Lưu ý rằng bạn phải sử dụng dấu ngoặc đơn cho các ví dụ trên.

Nếu bạn muốn loại bỏ mọi thứ trừ các ký tự ASCII có thể in cơ bản (tất cả các ký tự mẫu ở trên sẽ bị xóa), bạn có thể sử dụng:

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

Để tham khảo xem http://www.fileformat.info/info/charset/UTF-8/list.htmlm


1
Regrec của bạn xử lý tốt các ký tự UTF8; nhưng nó loại bỏ các ký tự "đặc biệt" không phải UTF8; như ç, ü và ö. '/[\x00-\x1F\x80-\xC0]/u'để chúng nguyên vẹn; nhưng cũng có dấu phân chia (F7) và phép nhân (D7).
Hazar

@Hazar vâng, bạn đúng \ x80- \ xFF bị tước quá nhiều, nhưng \ x80- \ xC0 vẫn còn quá hạn chế. Điều này sẽ bỏ lỡ các ký tự có thể in khác như © £ ±. Để tham khảo xem utf8-chartable.de
Dalin

1
@TimMopol vì PHP sẽ mở rộng các chuỗi ký tự đó: php.net/manual/en/, vì vậy regex sẽ không thấy phạm vi mà bạn đang cố gắng nói về nó.
Dalin

1
7F thì sao? Có nên không \x7F-\x9F?
Chuông

1
Tôi đã thử rất nhiều, tôi đã thử mọi chức năng mã hóa có sẵn trong PHP từ regex đến mb_ đến htmlspecialchars, v.v. Không có gì loại bỏ các ký tự điều khiển, cảm ơn vì đã đầu tư công việc.
Giăng

29

Bắt đầu với PHP 5.2, chúng tôi cũng có quyền truy cập vào bộ lọc_var mà tôi chưa thấy đề cập đến vì vậy tôi nghĩ rằng tôi sẽ ném nó ra khỏi đó. Để sử dụng bộ lọc_var để loại bỏ các ký tự không in được <32 và> 127, bạn có thể thực hiện:

Lọc các ký tự ASCII dưới 32

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

Lọc các ký tự ASCII trên 127

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

Dải cả hai:

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

Bạn cũng có thể mã hóa các ký tự thấp html (dòng mới, tab, v.v.) trong khi tước cao:

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

Ngoài ra còn có các tùy chọn để tước HTML, khử trùng e-mail và URL, v.v.

Vệ sinh: http://php.net/manual/en/filter.filters.sanitize.php

Xác thực: http://php.net/manual/en/filter.filters.validate.php

Tuy nhiên, vẫn còn một vấn đề, đó là FILTER_FLAG_STRIP_LOW sẽ loại bỏ các dòng mới và trả về vận chuyển, mà đối với một văn bản là các ký tự hoàn toàn hợp lệ ... do đó, một số câu trả lời của Regex, đôi khi vẫn cần thiết, ví dụ như sau khi xem xét chủ đề, tôi dự định làm điều này cho textareas:

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

Điều này có vẻ dễ đọc hơn một số biểu thức được loại bỏ theo phạm vi số.



18

Điều này đơn giản hơn:

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


5
Điều này cũng loại bỏ các nguồn cấp dữ liệu, trả về vận chuyển và ký tự UTF8.
Dalin

5
@Dalin Không có thứ gọi là ký tự UTF-8. Có các ký hiệu / ký tự Unicode và UTF-8 là một mã hóa có thể đại diện cho tất cả chúng. Bạn muốn nói dải này nằm ngoài phạm vi ASCII .
Mathias Bynens

1
Ăn các ký tự tiếng Ả Rập :)
Rolf

16

Tất cả các giải pháp hoạt động một phần, và thậm chí dưới đây có thể không bao gồm tất cả các trường hợp. Vấn đề của tôi là trong việc cố gắng chèn một chuỗi vào bảng mysf utf8. Chuỗi (và byte của nó) đều tuân thủ utf8, nhưng có một vài chuỗi xấu. Tôi cho rằng hầu hết trong số họ là kiểm soát hoặc định dạng.

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

Để làm trầm trọng thêm vấn đề là bảng so với máy chủ so với kết nối so với kết xuất nội dung, như đã nói về một chút ở đây


1
Người duy nhất vượt qua tất cả các bài kiểm tra đơn vị của tôi, tuyệt vời!
Korri

\ xE2 \ x80 [\ xA4- \ xA8] (hoặc 226.128. [164-168]) - là sai, trình tự bao gồm các ký hiệu có thể in tiếp theo: Ký tự Unicode 'ONE DOT LEADER' (U + 2024), Ký tự Unicode 'TWO DOT LÃNH ĐẠO '(U + 2025), Ký tự Unicode' HORIZONTAL ELLIPSIS '(U + 2026), Ký tự Unicode' ĐIỂM HYPHENATION '(U + 2027). Và chỉ có một thứ không thể in được: Ký tự Unicode 'LINE SEPARATOR' (U + 2028). Tiếp theo cũng không thể in được: Ký tự Unicode 'PARAGRAPH SEPARATOR' (U + 2029). Vì vậy, thay thế chuỗi bằng: \ xE2 \ x80 [\ xA8- \ xA9] \ xE2 \ x80 [\ xA8- \ xA9] để xóa LINE SEPARATOR và PARAGRAPH SEPARATOR.
MingalevME

Đây là giải pháp tốt nhất mà tôi có thể tìm thấy cho đến nay, nhưng tôi phải thêm vào $s = preg_replace('/(\xF0\x9F[\x00-\xFF][\x00-\xFF])/', ' ', $s);vì tất cả các nhân vật biểu tượng cảm xúc đã làm rối tung mysql
Joe Black

9

Phiên bản tuân thủ UTF-8 của tôi:

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


7
Điều này cũng loại bỏ các ký tự như dấu ngoặc kép, dấu ngoặc, vv Đó chắc chắn là các ký tự có thể in được.
Gajus

đây là điều tuyệt vời! nó đã cứu mạng tôi, rối tung lên trong khi in các ký tự tiếng Ả Rập, hoạt động như vô địch :)
krishna

6

Bạn có thể sử dụng biểu thức chính quy để xóa mọi thứ ngoài các ký tự bạn muốn giữ:

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

Thay thế mọi thứ không phải (^) các chữ cái AZ hoặc az, các số 0-9, dấu cách, dấu gạch dưới, hypen, dấu cộng và ký hiệu - không có gì (ví dụ: xóa nó).


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

Thao tác này sẽ xóa tất cả các ký tự điều khiển ( http://uk.php.net/manual/en/regapi.reference.unicode.php ) để lại các \nký tự dòng mới. Từ kinh nghiệm của tôi, các ký tự điều khiển là những ký tự thường gây ra sự cố in ấn.


1
Nó hoạt động hoàn hảo cho tôi! Tôi đã thêm chỉ /ucho ký tự UTF-8. Bạn có thể vui lòng giải thích những gì phần đầu tiên (?!\n)làm?
Marcio Mazzucato

4

Để loại bỏ tất cả các ký tự không phải ASCII khỏi chuỗi đầu vào

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

Mã đó sẽ loại bỏ bất kỳ ký tự nào trong các phạm vi hex 0-31 và 128-255, chỉ để lại các ký tự hex 32-127 trong chuỗi kết quả, mà tôi gọi là $ result trong ví dụ này.


3

Câu trả lời của @PaulDixon hoàn toàn sai , bởi vì nó loại bỏ các ký tự ASCII mở rộng có thể in 128-255! đã được sửa chữa một phần. Tôi không biết tại sao anh ta vẫn muốn xóa 128-255 khỏi bộ ASCII 127 bit có ký tự 127 vì nó không có các ký tự ASCII mở rộng.

Nhưng cuối cùng, điều quan trọng là không xóa 128-255 vì ví dụ chr(128)( \x80) là ký hiệu euro trong ASCII 8 bit và nhiều phông chữ UTF-8 trong Windows hiển thị ký hiệu euro và Android liên quan đến thử nghiệm của riêng tôi.

Và nó sẽ giết nhiều ký tự UTF-8 nếu bạn xóa ký tự ASCII 128-255 khỏi chuỗi UTF-8 (có thể là byte bắt đầu của ký tự UTF-8 nhiều byte). Vì vậy, đừng làm điều đó! Chúng là các ký tự hoàn toàn hợp pháp trong tất cả các hệ thống tệp hiện đang sử dụng. Phạm vi dành riêng duy nhất là 0-31 .

Thay vào đó, hãy sử dụng để xóa các ký tự không in được 0-31 và 127:

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

hoạt động trong ASCII và UTF-8 vì cả hai đều có chung phạm vi điều khiển .

Cách thay thế chậm nhất nhanh nhất mà không sử dụng biểu thức thông thường:

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

Nếu bạn muốn giữ lại tất cả các ký tự khoảng trắng \t, \n\r, sau đó loại bỏ chr(9), chr(10)chr(13)từ danh sách này. Lưu ý: Khoảng trắng thông thường là chr(32)để nó nằm trong kết quả. Tự quyết định nếu bạn muốn loại bỏ không gian không phá vỡ chr(160)vì nó có thể gây ra vấn đề.

Được thử nghiệm bởi @PaulDixon và được xác minh bởi chính tôi.


2

làm thế nào về:

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

cho tôi toàn quyền kiểm soát những gì tôi muốn đưa vào


0

Đánh dấu anwser là hoàn hảo nhưng nó bỏ lỡ ký tự 127 (DEL) cũng là một ký tự không in được

câu trả lời của tôi sẽ là

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

Câu trả lời này là sai, quá. Xem: stackoverflow.com/a/42058165/318765
mgutt

câu trả lời ở trên là một lời khen cho câu trả lời ban đầu chỉ thêm ký tự "xóa".
Mubashar

0

"Cedivad" đã giải quyết vấn đề cho tôi với kết quả liên tục của các ký tự Thụy Điển.

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

Cảm ơn!


0

Đối với bất cứ ai vẫn đang tìm cách làm điều này mà không loại bỏ các ký tự không in được, nhưng thoát khỏi chúng, tôi đã làm điều này để giúp đỡ. Hãy cải thiện nó! Các ký tự được thoát đến \\ x [A-F0-9] [A-F0-9].

Gọi như vậy:

$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

Tôi đã giải quyết vấn đề cho UTF8 bằng cách sử dụng https://github.com/neitanod/forceutf8

use ForceUTF8\Encoding;

$string = Encoding::fixUTF8($string);

1
Lib này chuyển đổi các ký tự có dấu UTF-8 và biểu tượng cảm xúc UTF-8 thành "?" ký hiệu. Vấn đề khá nghiêm trọng không may.
ChristoKiwi

0

Regex vào câu trả lời được chọn không thành công cho Unicode: 0x1d (với php 7.4)

một giải pháp:

<?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;

từ: Chuỗi UTF 8 xóa tất cả các ký tự vô hình trừ dòng mới

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.