현재 위치: > 최신 기사 목록> strcmp 함수가 멍청하게 보입니까? 캐릭터 인코딩 문제를 피하는 이유와 방법 공개

strcmp 함수가 멍청하게 보입니까? 캐릭터 인코딩 문제를 피하는 이유와 방법 공개

gitbox 2025-09-11

PHP의 중국어, 이모티콘 또는 톤을 포함하는 라틴 문자를 비교하기 위해 strcmp ()를 사용하면 "비교 결과가 잘못된 것", "디스플레이는 물음표/작은 정사각형"및 "이상한 정렬"과 같은 현상을 겪게됩니다. 많은 사람들이 이것을 "brudled code"라고 부릅니다. 실제로, strcmp () 자체는 "캐릭터를 엉망으로 만들 수있는 능력이 없다. 단지 두 줄의"생 바이너리 "를 비교한다. 문제는 종종 일관되지 않은 문자 인코딩 , 다른 텍스트 정규화 또는 잘못된 비교 도구 에 있습니다.

다음은 한 번에 주요 이유와 운영 솔루션을 설명합니다.

1. strcmp ()는 정확히 무엇을합니까?

  • 바이너리 안전, 사례 민감성 : strcmp ($ a, $ b)는 바이트 순서로 $ a$ b를 비교하여 <0 / 0 /> 0을 반환합니다. UTF-8, GBK, 이모티콘을 이해하지 못하거나 "알파벳 분류 규칙"을 이해하지 못하거나 케이스 폴딩 또는 악센트 처리도하지도 않습니다.

  • 결론 : 두 줄이 다른 인코딩을 사용하거나 UTF-8이지만 다른 바이트 시퀀스 (예 : BOM, 다른 정규화 된 형태)를 사용하면 strcmp () 의 결과가 "비합리적으로 보입니다".

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-string">"a"</span></span><span>, </span><span><span class="hljs-string">"b"</span></span><span>));   </span><span><span class="hljs-comment">// int(-1) 정상:a &lt; b</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-string">"중국인"</span></span><span>, </span><span><span class="hljs-string">"성격"</span></span><span>)); </span><span><span class="hljs-comment">// 비교적 UTF-8 的성격节序,결과는 일관되지 않을 수 있습니다“중국인성격拼音顺序”</span></span><span>
</span></span>

2. 일반적인 "갈색 코드/비교 이상"의 4 가지 주요 원인.

  1. 일관되지 않은 인코딩 (UTF-8 대 GBK 등)
    동일한 "중국어", 하나는 UTF-8이고 다른 하나는 GBK이고 바이트는 완전히 다릅니다. strcmp () 는 바이트 만 본다. 물론 "잘못 배치된다".

  2. UTF-8 BOM 및 보이지 않는 캐릭터 <br> 파일 헤더 또는 입력에 BOM (EF BB BF) , ZWSP (ZERL-Width Space) 및 보이지 않는 제어 문자를 삽입하면 첫 바이트/마지막 바이트를 다르게 만들어서 더 "터무니"가됩니다.

  3. 정규화 된 차이 (NFC/NFD)
    é는 단일 문자 (NFC) 또는 "E + Combined Accent"(NFD) 일 수 있습니다. 인간의 눈은 동일하고 바이트는 다르고 strcmp ()는 비정상적으로 불평등하거나 분류 된 것으로 판단됩니다.

  4. "인간 분류/언어 규칙"을 기대하지만 바이트 비교 <br>을 사용하십시오 중국인 Pinyin, German?, 프랑스 악센트, 일본 칸나 규칙으로 분류하고 싶습니까? strcmp ()는 이것을 이해하지 못하고 지역 비교 도구가 필요합니다.

3. 피하는 방법 : 구현할 수있는 솔루션 세트

1) UTF-8에 통합 (BOM 없음)

  • 입구 통합 : 데이터베이스 연결, HTTP 헤더, 템플릿 파일, CLI 환경, 전체 링크는 UTF-8 로 설정됩니다.

  • BOM/제어 문자 제거 :

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">strip_bom_and_controls</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-keyword">string</span></span></span><span> </span><span><span class="hljs-variable">$s</span></span><span>): </span><span><span class="hljs-title">string</span></span><span> {
    </span><span><span class="hljs-comment">// 가다 BOM</span></span><span>
    </span><span><span class="hljs-variable">$s</span></span><span> = </span><span><span class="hljs-title function_ invoke__">preg_replace</span></span><span>(</span><span><span class="hljs-string">'/^\xEF\xBB\xBF/'</span></span><span>, </span><span><span class="hljs-string">''</span></span><span>, </span><span><span class="hljs-variable">$s</span></span><span>);
    </span><span><span class="hljs-comment">// 가다常见零宽성격符:ZWSP, ZWNJ, ZWJ, NBSP …</span></span><span>
    </span><span><span class="hljs-variable">$s</span></span><span> = </span><span><span class="hljs-title function_ invoke__">preg_replace</span></span><span>(</span><span><span class="hljs-string">'/[\x{200B}\x{200C}\x{200D}\x{00A0}]/u'</span></span><span>, </span><span><span class="hljs-string">''</span></span><span>, </span><span><span class="hljs-variable">$s</span></span><span>);
    </span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-variable">$s</span></span><span>;
}
</span></span>
  • 필요한 경우 변환 :

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-variable">$clean</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mb_convert_encoding</span></span><span>(</span><span><span class="hljs-variable">$input</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>, </span><span><span class="hljs-string">'UTF-8,GBK,GB2312,BIG5,ISO-8859-1'</span></span><span>);
</span></span>

2) 유니 코드 정규화를 수행하십시오

  • Intl 확장자를 설치/활성화하고 Normanizer를 사용하여 NFC로 통합하십시오.

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">class_exists</span></span><span>(</span><span><span class="hljs-string">'Normalizer'</span></span><span>)) {
    </span><span><span class="hljs-variable">$a</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
    </span><span><span class="hljs-variable">$b</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
}
</span></span>

3) "정확한 비교 함수"를 선택하십시오.

  • 여전히 "바이트 일관성" : strcmp ()를 사용하십시오. 또는 사례에 민감한 바이트 비교를 원한다면 strcasecmp ()를 사용하십시오 (바이트, ASCII 규칙).

  • 인간 읽기 가능/언어 규칙의 비교 (분류/중복 제거/찾기) : intl \ collator를 사용합니다 (지역 비교, 악센트, 대문자, 대문자 및 변형).

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-variable">$coll</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title class_">\Collator</span></span><span>(</span><span><span class="hljs-string">'zh_CN'</span></span><span>);          </span><span><span class="hljs-comment">// 또는 'zh-Hans-CN', 'en_US', 'de_DE' 기다리다</span></span><span>
</span><span><span class="hljs-variable">$coll</span></span><span>-&gt;</span><span><span class="hljs-title function_ invoke__">setStrength</span></span><span>(</span><span><span class="hljs-title class_">\Collator</span></span><span>::</span><span><span class="hljs-variable constant_">SECONDARY</span></span><span>); </span><span><span class="hljs-comment">// 忽略大小写但区分重音기다리다</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-variable">$coll</span></span><span>-&gt;</span><span><span class="hljs-title function_ invoke__">compare</span></span><span>(</span><span><span class="hljs-string">'중국인'</span></span><span>, </span><span><span class="hljs-string">'성격'</span></span><span>));     </span><span><span class="hljs-comment">// -1/0/1,언어 규칙에 따라</span></span><span>
</span></span>
  • "사용자 비주얼에서 문자 수/자르기/트래버스" : grapeme_* 시리즈 ( INTL 과 동일)를 사용하여 "Grameme Clusters"를 처리하여 이모티콘을 절반으로 절단하지 않도록하십시오.

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-variable">$text</span></span><span> = </span><span><span class="hljs-string">"?????개발"</span></span><span>; </span><span><span class="hljs-comment">// 포함하다 ZWJ 커넥터 emoji</span></span><span>
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-title function_ invoke__">grapheme_substr</span></span><span>(</span><span><span class="hljs-variable">$text</span></span><span>, </span><span><span class="hljs-number">0</span></span><span>, </span><span><span class="hljs-number">2</span></span><span>); </span><span><span class="hljs-comment">// ?????열려 있는</span></span><span>
</span></span>
  • 무시 해야하는 다중 바이트 비교 : 비교하기 전에 MB_STRTOLOWER 또는 MB_CONVERT_CASE를 사용하십시오 (먼저 정규화해야합니다) :

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-variable">$a</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
</span><span><span class="hljs-variable">$b</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
</span><span><span class="hljs-variable">$aFold</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mb_strtolower</span></span><span>(</span><span><span class="hljs-variable">$a</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>);
</span><span><span class="hljs-variable">$bFold</span></span><span> = </span><span><span class="hljs-title function_ invoke__">mb_strtolower</span></span><span>(</span><span><span class="hljs-variable">$b</span></span><span>, </span><span><span class="hljs-string">'UTF-8'</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">strcmp</span></span><span>(</span><span><span class="hljs-variable">$aFold</span></span><span>, </span><span><span class="hljs-variable">$bFold</span></span><span>) === </span><span><span class="hljs-number">0</span></span><span>);
</span></span>

4) 정렬 및 희석 검사의 예제

중국인 Pinyin (도식)에 ​​의해 정렬

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-variable">$names</span></span><span> = [</span><span><span class="hljs-string">'장 산'</span></span><span>, </span><span><span class="hljs-string">'Li Si'</span></span><span>, </span><span><span class="hljs-string">'왕 우'</span></span><span>, </span><span><span class="hljs-string">'알리'</span></span><span>, </span><span><span class="hljs-string">'카오 카오'</span></span><span>];
</span><span><span class="hljs-variable">$coll</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title class_">\Collator</span></span><span>(</span><span><span class="hljs-string">'zh_CN@collation=pinyin'</span></span><span>); </span><span><span class="hljs-comment">// 시스템이 필요합니다 ICU 지원하다</span></span><span>
</span><span><span class="hljs-variable">$coll</span></span><span>-&gt;</span><span><span class="hljs-title function_ invoke__">sort</span></span><span>(</span><span><span class="hljs-variable">$names</span></span><span>);
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$names</span></span><span>);
</span></span>

대문자와 대문사 및 스트레스를 무시하는 "인간 중복 제거"

 <span><span><span class="hljs-meta">&lt;?php</span></span><span>
</span><span><span class="hljs-variable">$input</span></span><span> = [</span><span><span class="hljs-string">'café'</span></span><span>, </span><span><span class="hljs-string">'Cafe'</span></span><span>, </span><span><span class="hljs-string">'CAFé'</span></span><span>, </span><span><span class="hljs-string">'cafe'</span></span><span>];
</span><span><span class="hljs-variable">$coll</span></span><span> = </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-title class_">\Collator</span></span><span>(</span><span><span class="hljs-string">'fr_FR'</span></span><span>);
</span><span><span class="hljs-variable">$coll</span></span><span>-&gt;</span><span><span class="hljs-title function_ invoke__">setStrength</span></span><span>(</span><span><span class="hljs-title class_">\Collator</span></span><span>::</span><span><span class="hljs-variable constant_">PRIMARY</span></span><span>); </span><span><span class="hljs-comment">// 악센트와 대문자 케이스를 무시하십시오</span></span><span>
</span><span><span class="hljs-variable">$unique</span></span><span> = [];
</span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$input</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$s</span></span><span>) {
    </span><span><span class="hljs-variable">$sN</span></span><span> = </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-title function_ invoke__">normalize</span></span><span>(</span><span><span class="hljs-variable">$s</span></span><span>, </span><span><span class="hljs-title class_">Normalizer</span></span><span>::</span><span><span class="hljs-variable constant_">FORM_C</span></span><span>);
    </span><span><span class="hljs-variable">$found</span></span><span> = </span><span><span class="hljs-literal">false</span></span><span>;
    </span><span><span class="hljs-keyword">foreach</span></span><span> (</span><span><span class="hljs-variable">$unique</span></span><span> </span><span><span class="hljs-keyword">as</span></span><span> </span><span><span class="hljs-variable">$u</span></span><span>) {
        </span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$coll</span></span><span>-&gt;</span><span><span class="hljs-title function_ invoke__">compare</span></span><span>(</span><span><span class="hljs-variable">$sN</span></span><span>, </span><span><span class="hljs-variable">$u</span></span><span>) === </span><span><span class="hljs-number">0</span></span><span>) { </span><span><span class="hljs-variable">$found</span></span><span> = </span><span><span class="hljs-literal">true</span></span><span>; </span><span><span class="hljs-keyword">break</span></span><span>; }
    }
    </span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-variable">$found</span></span><span>) </span><span><span class="hljs-variable">$unique</span></span><span>[] = </span><span><span class="hljs-variable">$sN</span></span><span>;
}
</span><span><span class="hljs-title function_ invoke__">print_r</span></span><span>(</span><span><span class="hljs-variable">$unique</span></span><span>); </span><span><span class="hljs-comment">// 하나의 변형 만 보존됩니다</span></span><span>
</span></span>

4. Quick Error-Query List (먼저 "Barbled Code"를 살펴보십시오)

  1. 인코딩 확인 : mb_detect_encoding ($ s, [ 'utf-8', 'gbk', 'big5', 'iso-8859-1'], true) . 확실하지 않은 경우 먼저 UTF-8로 전송하십시오.

  2. BOM/ZERO-Width 문자로 이동 : 위의 Strip_Bom_and_Controls ()를 참조하십시오.

  3. NFC로 정규화 : Normalizer :: Normalize () .

  4. 비교 목표를 명확히하십시오 .

    • 바이트 일관성 : strcmp/strcasecmp .

    • 언어 규칙 정렬/평등 : intl \ Collator .

    • 시각적 문자 수준 처리 : Grapheme_* .

  5. 데이터베이스는 HTTP 헤더와 일치합니다 . MySQL은 UTF8MB4 와 적절한 Collation (예 : UTF8MB4_0900_AI_CI ) 및 HTTP SET Content-Type : Text/HTML; charset = utf-8 .

5. FAQ : 몇 가지 전형적인 "구덩이"

  • "그것은 같은 UTF-8입니다. 왜 strcmp () 가 같지 않습니까?"
    BOM, 제로 폭 2 자, 또는 하나는 NFC이고 다른 하나는 NFD와 혼합 될 수 있습니다. 먼저 청소 + 표준화.

  • " strcasecmp ()가 사례에 민감하게 국제화 될 수 있습니까?"
    접이는 주로 ASCII 시맨틱입니다. 보다 신뢰할 수있는 실습 : MB_STRTOLOWER () 후 비교하거나 적절한 콜라터 의 강도를 사용하십시오.

  • "이모티콘/콤비네이션 단어는 잘린 또는 비정상적으로 계산됩니다."
    Grapheme_strlen/Grapheme_Substr을 사용하고 Strlen/substr을 사용하여 사용자가 가시 가능한 문자를 처리하지 마십시오.