PHP : 원래 문자 집합을 몰라도 문자열을 UTF-8로 변환하거나 최소한 시도


146

전 세계의 클라이언트를 다루는 응용 프로그램이 있으며 데이터베이스에 들어가는 모든 것이 UTF-8로 인코딩되기를 원합니다.

나에게 가장 큰 문제는 문자열의 소스가 어떤 인코딩인지 알지 못한다는 것입니다-텍스트 상자에서 ( <form accept-charset="utf-8">사용자가 실제로 양식을 제출 한 경우에만 유용합니다) 또는 업로드 된 텍스트 파일에서 입력을 제어 할 수 없습니다.

필요한 것은 내 데이터베이스에 들어가는 것들이 가능한 한 UTF-8로 인코딩되도록하는 함수 또는 클래스입니다. 시도 iconv(mb_detect_encoding($text), "UTF-8", $text); 했지만 문제가 있습니다 (입력이 'fiancée'이면 'fianc'를 반환합니다). 나는 많은 것들을 시도했다 = /

파일 업로드의 경우 최종 사용자에게 사용하는 인코딩을 지정하도록 요청하고 출력이 어떻게 표시되는지 미리보기를 표시하는 아이디어를 좋아하지만 불쾌한 해커에게는 도움이되지 않습니다 (사실, 삶을 살릴 수 있음) 조금 더 쉽게).

주제에 대한 다른 SO 질문을 읽었지만 "RSS 피드를 구문 분석해야합니다"또는 "웹 사이트에서 데이터를 긁습니다"(또는 실제로는 "당신이 할 수 없음")와 같은 미묘한 차이가있는 것 같습니다.

그러나 적어도 좋은 시도 가 있어야합니다 !


5
정의 상으로는 절대적으로 정확한 결과를 얻는 것은 기본적으로 불가능합니다. 실제로 알 수없는 인코딩을 추측하는 성공률은 대단하지 않습니다. 휴리스틱을 사용할 수 있지만 100 % 미만 의 재료에 따라 시간의 100 % 미만이 정확합니다 . 당신은 그것을 알고 있어야합니다. 어쩌면 여기 누군가가 적어도 휴리스틱 스가 좋은 라이브러리를 제안 할 수 있습니다.
deceze

물론, 완벽한 해결책이 없다는 것을 알고 있습니다. 따라서 적어도 좋은 결과를 가져올 무언가에 대한 욕구가 있습니다.
잔인한 ...

이것은 도움이 될 수 있습니다 : stackoverflow.com/q/505562/642173
Melsi

UTF-8//IGNORE에서 2 번째 매개 변수로 사용해 보셨습니까 iconv?
발사

그래, 그게 내가 한 일이야. 분명 '완성품'이 '약혼자'가되는 것처럼 완벽하지는 않지만 확실히 더 좋습니다. TRANSLIT은 어떻게 작동하지 않습니까?
잔인한 ...

답변:


255

당신이 요구하는 것은 매우 어렵습니다. 가능하면 사용자가 인코딩을 지정하도록하는 것이 가장 좋습니다. 공격을 방지하는 것이 그렇게 쉬운 일이 아니거나 어렵지 않아야합니다.

그러나 이것을 시도해 볼 수 있습니다.

iconv(mb_detect_encoding($text, mb_detect_order(), true), "UTF-8", $text);

엄격하게 설정하면 더 나은 결과를 얻는 데 도움이 될 수 있습니다.


5
mb_detect_encodingPHP 배포판의 소스 코드를 살펴보십시오 (여기 : ext / mbstring / libmbfl / mbfl / mbfl_ident.c). 이 기능은 전혀 작동하지 않습니다. 일부 인코딩의 경우 "return true"도 있습니다. lol. 다른 것은 Ctrl + c Ctrl + v 기능에 있습니다. 사전과 같은 통계적 접근 방식 (내와 같은)이 없으면 인코딩을 감지 할 수 없기 때문입니다.
Oroboros102

1
내가 이해하는 방식 mb_detect_encoding은 제공된 인코딩 목록을 살펴보고 문자열에 잘못된 바이트 시퀀스가없는 첫 번째 인코딩을 허용합니다 ... ISO-8859-1과 같은 잘못된 바이트 시퀀스가없는 인코딩의 경우 항상 사실입니다 . "스마트 한"휴리스틱은 없으며 결과는 전달한 인코딩 목록 (및 순서)에 따라 크게 다릅니다.
wutz

이것은 나를 위해 일하는 것 같습니다. 내 사용자가 tinymce로 utf8 페이지에 텍스트를 제출했지만 알 수없는 이유로 utf8이 아닌 문자가 데이터베이스에 종종 표시되었습니다. 이 문제가 해결되었으므로 대단히 감사합니다.
giorgio79

@ Jeff Day-감사합니다. 내 무지를 용서하십시오. '엄격하게 설정하는 것'이란 무엇입니까?
Ash501

[제프의 날] 보내는 mb_detect_order()이이 PARAM의 기본값입니다하지만 그는 사실 (3 PARAM)에 엄격한 인코딩 감지를 설정하고 싶었 기 때문에 :),도
jave.web

28

조국 러시아에는 4 가지 인기있는 인코딩이 있으므로 귀하의 질문이 많이 필요합니다.

코드 페이지가 교차하기 때문에 문자의 문자 코드로만 인코딩을 감지 할 수 없습니다. 다른 언어로 된 일부 코드 페이지에는 완전한 교차점이 있습니다. 따라서 다른 접근 방식이 필요합니다 .

알 수없는 인코딩으로 작업 할 수있는 유일한 방법은 확률로 작업하는 것입니다. 따라서 "이 텍스트의 인코딩은 무엇입니까?"라는 질문에 대답하지 않고 "이 텍스트의 인코딩 가능성은 무엇 입니까?"를 이해하려고합니다 .

인기있는 러시아 기술 블로그의 한 사람 이이 접근법을 발명했습니다.

지원하려는 모든 인코딩에서 문자 코드의 확률 범위를 작성하십시오. 당신은 당신의 언어로 큰 텍스트를 사용하여 그것을 만들 수 있습니다 (예를 들어, 소설, 영어로 Shakespeare를 사용하고 러시아어로 lol Tolstoy를 사용하십시오). 당신은 이런 식으로 얻을 것입니다 :

    encoding_1:
    190 => 0.095249209893009,
    222 => 0.095249209893009,
    ...
    encoding_2:
    239 => 0.095249209893009,
    207 => 0.095249209893009,
    ...
    encoding_N:
    charcode => probabilty

다음. 알 수없는 인코딩으로 텍스트를 가져오고 "확률 사전"의 모든 인코딩에 대해 알 수없는 인코딩 된 텍스트에서 모든 심볼의 빈도를 검색합니다. 기호의 합 확률. 더 큰 등급의 인코딩이 승자가 될 것입니다. 더 큰 텍스트를위한 더 나은 결과.

관심 이 있으시면 기꺼이이 작업을 도와 드리겠습니다. 두 개의 문자 코드 확률 목록을 작성하여 정확도를 크게 높일 수 있습니다.

Btw. mb_detect_encoding이 작동하지 않습니다. 그렇습니다. "ext / mbstring / libmbfl / mbfl / mbfl_ident.c"에서 mb_detect_encoding 소스 코드를 살펴보십시오.


11

아마도 이것을 시도했지만 mb_convert_encoding 함수를 사용하지 않는 이유는 무엇입니까? 제공된 텍스트의 문자 세트를 자동 감지하려고 시도하거나 목록을 전달할 수 있습니다.

또한 나는 달리기를 시도했다.

$text = "fiancée";
echo mb_convert_encoding($text, "UTF-8");
echo "<br/><br/>";
echo iconv(mb_detect_encoding($text), "UTF-8", $text);

결과는 둘 다 동일합니다. 텍스트가 '가족'으로 잘리는 것을 어떻게 알 수 있습니까? DB 또는 브라우저에 있습니까?


데이터베이스에서 보이는 것처럼 보입니다. 방금 코드를 사용해 보았으며 동의합니다.
잔인한 ...

1
테이블 / 열에 정의한 데이터 정렬이 UTF-8인지 확인하십시오.
Alexey Gerasimov

@AlexeyGerasimov 정말 조사해야 할 것 같아요 iconv. 거의 순수한 mb_ * 방식으로 시도했습니다. 당신은 어떻게 생각하세요?
Anthony Rutledge

5

완전히 정확한 문자열의 문자셋을 식별 할 수있는 방법이 없습니다. 문자셋을 추측하는 방법이 있습니다. 이 방법들 중 하나, 그리고 아마도 현재 / 현재 PHP에서 가장 좋은 방법은 mb_detect_encoding ()입니다. 문자열을 스캔하여 특정 문자 집합에 고유 한 항목이 있는지 찾습니다. 당신의 문자열에 따라, 그러한 구별 가능한 사건이 없을 수 있습니다.

ISO-8859-1 문자 세트와 ISO-8859-15 비교 ( http://en.wikipedia.org/wiki/ISO/IEC_8859-15#Changes_from_ISO-8859-1 )

소수의 다른 문자 만 있으며 더 악화시키기 위해 동일한 바이트로 표시됩니다. 바이트 0xA4가 문자열에서 ¤ 또는 €을 의미하는지 여부를 인코딩하지 않고 문자열을 제공받을 수있는 방법이 없으므로 정확한 문자 집합을 알 수있는 방법이 없습니다.

(참고 : 캐릭터가 ¤ 또는 € 여야하지만 주변 캐릭터를 기준으로 알아 내기 위해 인적 요소 또는 훨씬 고급 스캔 기술 (예 : Oroboros102가 제안한 것)을 추가 할 수 있습니다. 너무 멀어)

예를 들어 UTF-8과 ISO-8859-1 사이에는 더 뚜렷한 차이점이 있으므로 확실하지 않은 경우이를 파악하는 것이 좋습니다.

재미있는 읽을 거리 : http://kore-nordmann.de/blog/php_charset_encoding_FAQ.html#how-do-i-determine-the-charset-encoding-of-a-string

올바른 문자셋을 보장하는 다른 방법이 있습니다. 양식과 관련하여 가능한 한 UTF-8을 시행하십시오 (모든 브라우저에서 제출이 UTF-8인지 확인하기 위해 눈사람을 확인하십시오 : http://intertwingly.net/blog/2010/07/29/Rails-and -Snowmen ) 완료되면 적어도 양식을 통해 제출 된 모든 텍스트가 utf_8인지 확인할 수 있습니다. 업로드 된 파일에 대해서는 exec () (가능한 경우 서버에서)를 통해 unix 'file -i'명령을 실행하여 탐지를 도와줍니다 (문서의 BOM 사용). 스크래핑 데이터에 대해서는 HTTP 헤더를 읽을 수 있습니다. 일반적으로 문자셋을 지정합니다. XML 파일을 구문 분석 할 때 XML 메타 데이터에 문자 세트 정의가 포함되어 있는지 확인하십시오.

자동으로 문자셋을 추측하려고하기보다는, 가능한 경우 특정 문자셋을 직접 확보하거나 감지에 의존하기 전에 (해당되는 경우) 소스에서 정의를 얻으려고 시도해야합니다.


암호화 된 데이터가있는 양식 및 이메일 등록 링크 여기에서 입력을 UTF-8로 만들거나 아무것도하지 않으려 고합니다. 내 대답은 어때? 유용한 의견에 감사드립니다. 감사.
Anthony Rutledge

3

정말 좋은 답변이 있으며 여기에 귀하의 질문에 대한 답변을 시도합니다. 나는 인코딩 마스터가 아니지만 순수한 UTF-8 스택을 데이터베이스에 가져 가고 싶다는 당신의 이해를 이해합니다 . utf8mb4테이블, 필드 및 연결에 MySQL의 인코딩을 사용 하고 있습니다.

"상황은 데이터가 HTML 양식 또는 전자 메일 등록 링크에서 올 때 UTF-8을 처리하기 위해 소독제, 유효성 검사기, 비즈니스 논리 및 준비된 명령문이 필요합니다." 그래서 간단한 방법으로이 아이디어로 시작했습니다.

  1. 인코딩을 감지하려고 시도하십시오. $encodings = ['UTF-8', 'ISO-8859-1', 'ASCII'];
  2. 인코딩이 감지되지 않으면 throw new RuntimeException
  3. 입력이 계속 UTF-8되면 계속하십시오.
  4. 그렇지 않으면 ISO-8859-1또는ASCII

    ㅏ. UTF-8 로의 변환을 시도하십시오 (대기, 완료되지 않음)

    비. 변환 된 값의 인코딩을 감지

    씨. 보고 된 인코딩 및 변환 된 값이 모두 인 UTF-8경우 계속하십시오.

    디. 그밖에,throw new RuntimeException

내 추상 수업에서 Sanitizer

살균제

    private function isUTF8($encoding, $value)
    {
        return (($encoding === 'UTF-8') && (utf8_encode(utf8_decode($value)) === $value));
    }

    private function utf8tify(&$value)
    {
        $encodings = ['UTF-8', 'ISO-8859-1', 'ASCII'];

        mb_internal_encoding('UTF-8');
        mb_substitute_character(0xfffd); //REPLACEMENT CHARACTER
        mb_detect_order($encodings);

        $stringEncoding = mb_detect_encoding($value, $encodings, true);

        if (!$stringEncoding) {
            $value = null;
            throw new \RuntimeException("Unable to identify character encoding in sanitizer.");
        }

        if ($this->isUTF8($stringEncoding, $value)) {
            return;
        } else {
            $value = mb_convert_encoding($value, 'UTF-8', $stringEncoding);
            $stringEncoding = mb_detect_encoding($value, $encodings, true);

            if ($this->isUTF8($stringEncoding, $value)) {
                return;
            } else {
                $value = null;
                throw new \RuntimeException("Unable to convert character encoding from ISO-8859-1, or ASCII, to UTF-8 in Sanitizer.");
            }
        }

        return;
    }

인코딩 문제 를 추상 Sanitizer클래스와 분리 하고 Encoder객체를의 구체적인 자식 인스턴스에 삽입 해야한다고 주장 할 수 Sanitizer있습니다. 그러나 내 접근 방식의 주요 문제는 더 많은 지식이 없으면 원하지 않는 인코딩 유형을 거부한다는 것입니다 (PHP mb_ * 함수에 의존하고 있음). 더 이상의 연구가 없다면, 그것이 일부 인구에 해를 끼치는 지 아닌지 (또는 중요한 정보를 잃어버린 경우) 알 수 없습니다. 그래서 더 배울 필요가 있습니다. 이 기사를 찾았습니다.

모든 프로그래머가 텍스트로 작업하기 위해 인코딩 및 문자 세트에 대해 긍정적으로 알아야 할 사항

또한 암호화 된 데이터가 전자 메일 등록 링크에 추가되면 ( OpenSSL또는 사용 mcrypt) 어떻게됩니까? 이것이 디코딩을 방해 할 수 있습니까? Windows-1252는 어떻습니까? 보안 영향은 어떻습니까? 사용 utf8_decode()하고 utf8_encode()있는이 Sanitizer::isUTF8모호한 있습니다.

사람들은 PHP mb_ * 함수의 단점을 지적했습니다. 조사하는 데 시간 iconv이 걸리지 않았지만 mb_ * functions보다 효과가 좋으면 알려주십시오.


나는 이 문제에 대한 stackoverflow.com/a/3521396/1429677 훌륭한 답변을 찾았습니다 . 여기 lib github.com/neitanod/forceutf8
Llewellyn

2

나에게 가장 큰 문제는 문자열의 소스가 어떤 인코딩인지 알지 못한다는 것입니다-텍스트 상자에서 (사용자가 실제로 양식을 제출 한 경우에만 유용합니다) 또는 업로드 된 텍스트 파일에서 입력을 제어 할 수 없습니다.

나는 그것이 문제라고 생각하지 않습니다. 응용 프로그램은 입력 소스를 알고 있습니다. 양식에서 온 경우에는 UTF-8 인코딩을 사용하십시오. 작동합니다. 제공된 데이터가 올바르게 인코딩되어 있는지 확인하십시오 (확인). 모든 데이터베이스가 전체 범위에서 UTF-8을 지원하는 것은 아닙니다.

파일 인 경우 UTF-8 인코딩 된 데이터베이스로 저장하지 않고 이진 형식으로 저장합니다. 파일을 다시 출력 할 때 이진 출력도 사용하면 완전히 투명합니다.

당신은 사용자가 인코딩을 말할 수 있다는 것이 기쁩니다. 파일을 다운로드 한 후 바이너리 파일이므로 어쨌든 말할 수 있기 때문입니다.

따라서 귀하의 질문에 대해 제기 한 특정 문제가 보이지 않는다는 것을 인정해야합니다. 그러나 문제가 무엇인지 더 자세히 설명 할 수 있습니다.


내 답변을보고 발행 하시겠습니까? 건설적인 의견에 감사드립니다. 감사.
Anthony Rutledge

1

사용중인 인코딩을 추측하기 위해 일련의 메트릭을 설정할 수 있습니다. 다시 말하지만, 완벽하지는 않지만 mb_detect_encoding ()에서 누락 된 부분을 잡을 수 있습니다.


그렇습니다. 그리움에 대해 말하면 mb_detect_encoding(), 내 대답은 여름에 사하라 사막에서 눈덩이가 올 가능성이 있다고 생각합니까?
Anthony Rutledge

1

"콘솔에 가져 가려면"을 추천 enca합니다. 다소 단순하지는 않지만 mb_detect_encoding, "파싱, 통계 분석, 추측 및 흑 마법의 혼합을 사용하여 인코딩을 결정합니다"(lol- man man page 참조 ). 그러나 이러한 국가 별 인코딩을 감지하려면 일반적으로 입력 파일의 언어를 전달해야합니다. (그러나 mb_detect_encoding인코딩이 감지 될 수 있도록 전달 된 인코딩 목록의 "올바른 위치"에 인코딩이 표시되어야하기 때문에 본질적으로 동일한 요구 사항이 있습니다.)

enca또한 여기에 나타났습니다 : 스크립트를 통해 Unix에서 파일 인코딩을 찾는 방법


1

귀하의 질문에 대한 답변이 겉으로 보이지만 사례를 단순화 할 수있는 접근법이 있습니다.

mysql에서 문자열 데이터를 반환하려고하는 비슷한 문제가 있었고 데이터베이스와 PHP를 모두 구성하여 utf-8 형식의 문자열을 반환하도록했습니다. 내가 오류를 얻은 유일한 방법은 실제로 데이터베이스에서 오류를 반환하는 것입니다.

마지막으로, 웹을 통한 항해는 그것을 다루는 정말 쉬운 방법을 찾았습니다.

mysql에 모든 유형의 문자열 데이터를 다른 형식과 데이터 정렬로 저장할 수 있다는 것을 제공하기 위해 PHP 연결 파일에서 다음과 같이 데이터 정렬을 utf-8로 설정하면됩니다.

$connection = new mysqli($server, $user, $pass, $db);
$connection->set_charset("utf8");

Wich는 먼저 데이터를 임의의 형식 또는 데이터 정렬로 저장하고 PHP 파일로 돌아올 때만 변환한다는 것을 의미합니다.

도움이 되었기를 바랍니다.



-2
public function convertToUtf8($text) {
    if(!$this->html)
        $this->html = cURL('http://'.$this->url, array('timeout' => 15));

    $html = $this->html;
    preg_match('/<meta.*?charset=(|\")(.*?)("|\")/i', $html, $matches);

    $charset = $matches[2];

    if($charset)
        return mb_convert_encoding($text, 'UTF-8', $charset);
    else
        return $text;
}

cURL 기본 옵션 :

curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);

나는 이와 같은 것을 시도했다. 그것은 나를 도왔다. 메타 문자 집합 정보에서 발견되면 변환 중입니다. 그렇지 않으면 아무것도하지 않습니다.


errr, 기능을 확인하고 변수를 정정 할 수 있습니까?
Martin

$ url은 무엇입니까? $ html이란 무엇입니까?
Martin
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.