development

문자열에서 UTF8이 아닌 문자 제거

big-blog 2020. 8. 19. 22:27
반응형

문자열에서 UTF8이 아닌 문자 제거


제대로 표시되지 않는 문자열에서 UTF8이 아닌 문자를 제거하는 데 문제가 있습니다. 문자는 다음과 같습니다. 0x97 0x61 0x6C 0x6F (16 진수 표현)

제거하는 가장 좋은 방법은 무엇입니까? 정규 표현식이나 다른 것?


정규식 접근 방식 사용 :

$regex = <<<'END'
/
  (
    (?: [\x00-\x7F]                 # single-byte sequences   0xxxxxxx
    |   [\xC0-\xDF][\x80-\xBF]      # double-byte sequences   110xxxxx 10xxxxxx
    |   [\xE0-\xEF][\x80-\xBF]{2}   # triple-byte sequences   1110xxxx 10xxxxxx * 2
    |   [\xF0-\xF7][\x80-\xBF]{3}   # quadruple-byte sequence 11110xxx 10xxxxxx * 3 
    ){1,100}                        # ...one or more times
  )
| .                                 # anything else
/x
END;
preg_replace($regex, '$1', $text);

UTF-8 시퀀스를 검색하여 그룹 1로 캡처합니다. 또한 UTF-8 시퀀스의 일부로 식별 할 수없는 단일 바이트와 일치하지만 캡처하지는 않습니다. 대체는 그룹 1로 캡처 된 모든 것입니다. 이는 유효하지 않은 모든 바이트를 효과적으로 제거합니다.

유효하지 않은 바이트를 UTF-8 문자로 인코딩하여 문자열을 복구 할 수 있습니다. 그러나 오류가 무작위이면 이상한 기호가 남을 수 있습니다.

$regex = <<<'END'
/
  (
    (?: [\x00-\x7F]               # single-byte sequences   0xxxxxxx
    |   [\xC0-\xDF][\x80-\xBF]    # double-byte sequences   110xxxxx 10xxxxxx
    |   [\xE0-\xEF][\x80-\xBF]{2} # triple-byte sequences   1110xxxx 10xxxxxx * 2
    |   [\xF0-\xF7][\x80-\xBF]{3} # quadruple-byte sequence 11110xxx 10xxxxxx * 3 
    ){1,100}                      # ...one or more times
  )
| ( [\x80-\xBF] )                 # invalid byte in range 10000000 - 10111111
| ( [\xC0-\xFF] )                 # invalid byte in range 11000000 - 11111111
/x
END;
function utf8replacer($captures) {
  if ($captures[1] != "") {
    // Valid byte sequence. Return unmodified.
    return $captures[1];
  }
  elseif ($captures[2] != "") {
    // Invalid byte of the form 10xxxxxx.
    // Encode as 11000010 10xxxxxx.
    return "\xC2".$captures[2];
  }
  else {
    // Invalid byte of the form 11xxxxxx.
    // Encode as 11000011 10xxxxxx.
    return "\xC3".chr(ord($captures[3])-64);
  }
}
preg_replace_callback($regex, "utf8replacer", $text);

편집하다:

  • !empty(x)비어 있지 않은 값과 일치합니다 ( "0"비어있는 것으로 간주 됨).
  • x != ""를 포함하여 비어 있지 않은 값과 일치 "0"합니다.
  • x !== ""를 제외한 모든 항목과 일치 ""합니다.

x != "" 이 경우에 사용하기에 가장 좋은 것 같습니다.

나는 또한 경기 속도를 약간 높였습니다. 각 문자를 개별적으로 일치시키는 대신 유효한 UTF-8 문자 시퀀스와 일치시킵니다.


utf8_encode()이미 UTF8 문자열에 적용 하면 잘못된 UTF8 출력이 반환됩니다.

이 모든 문제를 해결하는 기능을 만들었습니다. 라고 Encoding::toUTF8()합니다.

문자열의 인코딩이 무엇인지 알 필요가 없습니다. Latin1 (ISO8859-1), Windows-1252 또는 UTF8이거나 문자열에 이들을 혼합 할 수 있습니다. Encoding::toUTF8()모든 것을 UTF8로 변환합니다.

서비스가 데이터 피드를 모두 엉망으로 제공하여 동일한 문자열에서 인코딩을 혼합했기 때문에 그렇게했습니다.

용법:

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::toUTF8($mixed_string);

$latin1_string = Encoding::toLatin1($mixed_string);

또 다른 함수 인 Encoding :: fixUTF8 ()을 포함했습니다.이 함수는 UTF8로 여러 번 인코딩 된 왜곡 된 제품으로 보이는 모든 UTF8 문자열을 수정합니다.

용법:

require_once('Encoding.php'); 
use \ForceUTF8\Encoding;  // It's namespaced now.

$utf8_string = Encoding::fixUTF8($garbled_utf8_string);

예 :

echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");
echo Encoding::fixUTF8("FÃÂédÃÂération Camerounaise de Football");
echo Encoding::fixUTF8("Fédération Camerounaise de Football");

다음을 출력합니다.

Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football
Fédération Camerounaise de Football

다운로드 :

https://github.com/neitanod/forceutf8


mbstring을 사용할 수 있습니다.

$text = mb_convert_encoding($text, 'UTF-8', 'UTF-8');

... 유효하지 않은 문자를 제거합니다.

참조 : 유효하지 않은 UTF-8 문자를 물음표로 대체하면 mbstring.substitute_character가 무시 된 것 같습니다.


이 함수는 모든 비 ASCII 문자를 제거합니다. 유용하지만 질문을 해결하지는 못합니다.
이것은 인코딩에 관계없이 항상 작동하는 내 함수입니다.

function remove_bs($Str) {  
  $StrArr = str_split($Str); $NewStr = '';
  foreach ($StrArr as $Char) {    
    $CharNo = ord($Char);
    if ($CharNo == 163) { $NewStr .= $Char; continue; } // keep £ 
    if ($CharNo > 31 && $CharNo < 127) {
      $NewStr .= $Char;    
    }
  }  
  return $NewStr;
}

작동 원리 :

echo remove_bs('Hello õhowå åare youÆ?'); // Hello how are you?

$text = iconv("UTF-8", "UTF-8//IGNORE", $text);

이것이 내가 사용하는 것입니다. 꽤 잘 작동하는 것 같습니다. http://planetozh.com/blog/2005/01/remove-invalid-characters-in-utf-8/ 에서 가져옴


이 시도:

$string = iconv("UTF-8","UTF-8//IGNORE",$string);

iconv manual 에 따르면 이 함수는 첫 번째 매개 변수를 입력 문자 집합으로, 두 번째 매개 변수를 출력 문자 집합으로, 세 번째 매개 변수를 실제 입력 문자열로 사용합니다.

입력 및 출력 문자 집합을 모두 UTF-8로 설정하고 출력 문자 집합 //IGNORE플래그를 추가 하면 함수는 출력 문자 집합 으로 표시 할 수없는 입력 문자열의 모든 문자를 삭제 (스트립)합니다. 따라서 실제로 입력 문자열을 필터링합니다.


텍스트에는 UTF8이 아닌 문자가 포함될 수 있습니다 . 먼저 시도하십시오.

$nonutf8 = mb_convert_encoding($nonutf8 , 'UTF-8', 'UTF-8');

여기에서 자세한 내용을 읽을 수 있습니다. http://php.net/manual/en/function.mb-convert-encoding.php 뉴스


UConverter는 PHP 5.5부터 사용할 수 있습니다. intl 확장자를 사용하고 mbstring을 사용하지 않는 경우 UConverter가 더 나은 선택입니다.

function replace_invalid_byte_sequence($str)
{
    return UConverter::transcode($str, 'UTF-8', 'UTF-8');
}

function replace_invalid_byte_sequence2($str)
{
    return (new UConverter('UTF-8', 'UTF-8'))->convert($str);
}

htmlspecialchars can be used to remove invalid byte sequence since PHP 5.4. Htmlspecialchars is better than preg_match for handling large size of byte and the accuracy. A lot of the wrong implementation by using regular expression can be seen.

function replace_invalid_byte_sequence3($str)
{
    return htmlspecialchars_decode(htmlspecialchars($str, ENT_SUBSTITUTE, 'UTF-8'));
}

I have made a function that deletes invalid UTF-8 characters from a string. I'm using it to clear description of 27000 products before it generates the XML export file.

public function stripInvalidXml($value) {
    $ret = "";
    $current;
    if (empty($value)) {
        return $ret;
    }
    $length = strlen($value);
    for ($i=0; $i < $length; $i++) {
        $current = ord($value{$i});
        if (($current == 0x9) || ($current == 0xA) || ($current == 0xD) || (($current >= 0x20) && ($current <= 0xD7FF)) || (($current >= 0xE000) && ($current <= 0xFFFD)) || (($current >= 0x10000) && ($current <= 0x10FFFF))) {
                $ret .= chr($current);
        }
        else {
            $ret .= "";
        }
    }
    return $ret;
}

$string = preg_replace('~&([a-z]{1,2})(acute|cedil|circ|grave|lig|orn|ring|slash|th|tilde|uml);~i', '$1', htmlentities($string, ENT_COMPAT, 'UTF-8'));

From recent patch to Drupal's Feeds JSON parser module:

//remove everything except valid letters (from any language)
$raw = preg_replace('/(?:\\\\u[\pL\p{Zs}])+/', '', $raw);

If you're concerned yes it retains spaces as valid characters.

Did what I needed. It removes widespread nowadays emoji-characters that don't fit into MySQL's 'utf8' character set and that gave me errors like "SQLSTATE[HY000]: General error: 1366 Incorrect string value".

For details see https://www.drupal.org/node/1824506#comment-6881382


So the rules are that the first UTF-8 octlet has the high bit set as a marker, and then 1 to 4 bits to indicate how many additional octlets; then each of the additional octlets must have the high two bits set to 10.

The pseudo-python would be:

newstring = ''
cont = 0
for each ch in string:
  if cont:
    if (ch >> 6) != 2: # high 2 bits are 10
      # do whatever, e.g. skip it, or skip whole point, or?
    else:
      # acceptable continuation of multi-octlet char
      newstring += ch
    cont -= 1
  else:
    if (ch >> 7): # high bit set?
      c = (ch << 1) # strip the high bit marker
      while (c & 1): # while the high bit indicates another octlet
        c <<= 1
        cont += 1
        if cont > 4:
           # more than 4 octels not allowed; cope with error
      if !cont:
        # illegal, do something sensible
      newstring += ch # or whatever
if cont:
  # last utf-8 was not terminated, cope

This same logic should be translatable to php. However, its not clear what kind of stripping is to be done once you get a malformed character.


To remove all Unicode characters outside of the Unicode basic language plane:

$str = preg_replace("/[^\\x00-\\xFFFF]/", "", $str);

Slightly different to the question, but what I am doing is to use HtmlEncode(string),

pseudo code here

var encoded = HtmlEncode(string);
encoded = Regex.Replace(encoded, "&#\d+?;", "");
var result = HtmlDecode(encoded);

input and output

"Headlight\x007E Bracket, &#123; Cafe Racer<> Style, Stainless Steel 中文呢?"
"Headlight~ Bracket, &#123; Cafe Racer<> Style, Stainless Steel 中文呢?"

I know it's not perfect, but does the job for me.


Welcome to 2019 and the /u modifier in regex which will handle UTF-8 multibyte chars for you

If you only use mb_convert_encoding($value, 'UTF-8', 'UTF-8') you will still end up with non-printable chars in your string

This method will:

  • Remove all invalid UTF-8 multibyte chars with mb_convert_encoding
  • Remove all non-printable chars like \r, \x00 (NULL-byte) and other control chars with preg_replace

method:

function utf8_filter(string $value): string{
    return preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($value, 'UTF-8', 'UTF-8'));
}

[:print:] match all printable chars and \n newlines and strip everything else

You can see the ASCII table below.. The printable chars range from 32 to 127, but newline \n is a part of the control chars which range from 0 to 31 so we have to add newline to the regex /[^[:print:]\n]/u

https://cdn.shopify.com/s/files/1/1014/5789/files/Standard-ASCII-Table_large.jpg?10669400161723642407

You can try to send strings through the regex with chars outside the printable range like \x7F (DEL), \x1B (Esc) etc. and see how they are stripped

function utf8_filter(string $value): string{
    return preg_replace('/[^[:print:]\n]/u', '', mb_convert_encoding($value, 'UTF-8', 'UTF-8'));
}

$arr = [
    'Danish chars'          => 'Hello from Denmark with æøå',
    'Non-printable chars'   => "\x7FHello with invalid chars\r \x00"
];

foreach($arr as $k => $v){
    echo "$k:\n---------\n";

    $len = strlen($v);
    echo "$v\n(".$len.")\n";

    $strip = utf8_decode(utf8_filter(utf8_encode($v)));
    $strip_len = strlen($strip);
    echo $strip."\n(".$strip_len.")\n\n";

    echo "Chars removed: ".($len - $strip_len)."\n\n\n";
}

https://www.tehplayground.com/q5sJ3FOddhv1atpR


How about iconv:

http://php.net/manual/en/function.iconv.php

Haven't used it inside PHP itself but its always performed well for me on the command line. You can get it to substitute invalid characters.

참고URL : https://stackoverflow.com/questions/1401317/remove-non-utf8-characters-from-string

반응형