"유사한"유니 코드 문자를 비교하는 방법은 무엇입니까?


94

나는 놀라운 문제에 빠진다.

내 응용 프로그램에 텍스트 파일을로드했고 µ 값을 비교하는 논리가 있습니다.

그리고 나는 텍스트가 동일하더라도 비교 값이 거짓임을 깨달았습니다.

 Console.WriteLine("μ".Equals("µ")); // returns false
 Console.WriteLine("µ".Equals("µ")); // return true

뒷줄에서 문자 µ는 복사 붙여 넣기됩니다.

그러나 이것들은 이와 같은 유일한 캐릭터가 아닐 수도 있습니다.

C #에서 동일하게 보이지만 실제로 다른 문자를 비교할 수있는 방법이 있습니까?


158
Schrödinger의 뮤를 찾은 것 같습니다.
BoltClock

19
서로 다른 문자입니다. 똑같아 보이지만 문자 코드가 다릅니다.
user2864740 dec.

93
유니 코드에 오신 것을 환영합니다.
ta.speot.is

11
무엇을 성취하고 싶습니까? 그 두 사람이 같으면 문자 코드도 다르지만 얼굴은 같을까요?
Jade

28
"같아 보임"과 "같아 보임"은 모호한 개념입니다. 글리프의 정체성을 의미합니까, 아니면 유사한 유사성을 의미합니까? 얼마나 가까이? 두 문자는 일부 글꼴에서 동일한 글리프를 가질 수 있으며 다른 글꼴에서는 매우 유사하며 또 다른 글꼴에서는 매우 유사하지 않을 수 있습니다. 중요한 것은 그런 비교를하고 어떤 맥락에서 (그리고 거짓 긍정과 거짓 부정의 수용 가능성)입니다.
유카 K. 펠라

답변:


125

많은 경우에, 당신은 할 수 있습니다 정상화 를 비교하기 전에 특정 정상화 폼에 유니 코드 문자를 모두, 그들은 일치 할 수 있어야한다. 물론 어떤 정규화 형식을 사용해야하는지는 문자 자체에 따라 다릅니다. 단지 그들이 때문에 필요가 같은 문자를 나타내는 의미하지 않는다 모두. 또한 사용 사례에 적합한 지 고려해야합니다. Jukka K. Korpela의 의견을 참조하십시오.

이 특정 상황에서 Tony 's answer 의 링크를 참조하면 U + 00B5에 대한 표에 다음과 같은 내용이 표시됩니다 .

분해 <compat> GREEK SMALL LETTER MU (U + 03BC)

즉, 원래 비교에서 두 번째 문자 인 U + 00B5를 첫 번째 문자 인 U + 03BC로 분해 할 수 있습니다.

따라서 정규화 형식 KC 또는 KD와 함께 완전한 호환성 분해를 사용하여 문자를 정규화합니다. 다음은 내가 시연하기 위해 작성한 간단한 예입니다.

using System;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        char first = 'μ';
        char second = 'µ';

        // Technically you only need to normalize U+00B5 to obtain U+03BC, but
        // if you're unsure which character is which, you can safely normalize both
        string firstNormalized = first.ToString().Normalize(NormalizationForm.FormKD);
        string secondNormalized = second.ToString().Normalize(NormalizationForm.FormKD);

        Console.WriteLine(first.Equals(second));                     // False
        Console.WriteLine(firstNormalized.Equals(secondNormalized)); // True
    }
}

유니 코드 정규화 및 다양한 정규화 형식에 대한 자세한 내용은 System.Text.NormalizationForm유니 코드 사양을 참조하십시오 .


26
유니 코드 사양 링크에 감사드립니다. 처음으로 읽었습니다. 작은 메모 : "정규화 양식 KC 및 KD는 임의의 텍스트에 맹목적으로 적용되어서는 안됩니다 .. 이러한 정규화 양식을 대문자 또는 소문자 매핑과 비슷하다고 생각하는 것이 가장 좋습니다. 특정 컨텍스트에서 핵심 의미를 식별하는 데 유용하지만 수행 항상 적절하지 않을 수있는 텍스트 수정. "
user2864740 dec.

149

똑같아도 정말 다른 기호이기 때문에 첫 번째는 실제 문자이고 char가 code = 956 (0x3BC)있고 두 번째는 마이크로 기호이며 181 (0xB5).

참조 :

따라서 이들을 비교하고 동일해야하는 경우 수동으로 처리하거나 비교하기 전에 한 문자를 다른 문자로 대체해야합니다. 또는 다음 코드를 사용하십시오.

public void Main()
{
    var s1 = "μ";
    var s2 = "µ";

    Console.WriteLine(s1.Equals(s2));  // false
    Console.WriteLine(RemoveDiacritics(s1).Equals(RemoveDiacritics(s2))); // true 
}

static string RemoveDiacritics(string text) 
{
    var normalizedString = text.Normalize(NormalizationForm.FormKC);
    var stringBuilder = new StringBuilder();

    foreach (var c in normalizedString)
    {
        var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
        if (unicodeCategory != UnicodeCategory.NonSpacingMark)
        {
            stringBuilder.Append(c);
        }
    }

    return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}

그리고 데모


11
호기심에서 두 개의 µ 기호를 갖는 이유는 무엇입니까? "Kilo sign"이라는 이름의 전용 K가 표시되지 않습니다 (아니요?).
MartinHaTh

12
@MartinHaTh : Wikipedia에 따르면 "역사적인 이유로" 입니다.
BoltClock

12
유니 코드에는 ISO 8859-1 과 같은 이전 문자 집합에서 가져온 많은 호환성 문자가 있어 이러한 문자 집합에서 쉽게 변환 할 수 있습니다. 문자 집합이 8 비트로 제한되었을 때 가장 일반적인 수학 및 과학적 사용을 위해 몇 개의 글리프 (예 : 그리스 문자)가 포함되었습니다. 외형을 기반으로 한 글리프 재사용이 일반적이어서 특수한 'K'가 추가되지 않았습니다. 하지만 항상 해결 방법이었습니다. "micro"에 대한 올바른 기호는 실제 그리스어 소문자 mu이고 Ohm에 대한 올바른 기호는 실제 대문자 오메가입니다.
VGR

8
히스테리 건포도를 위해 무언가를 할 때보 다 더 좋은 것은 없습니다
paulm dec

11
시리얼에 특별한 K가 있습니까?

86

둘 다 다른 문자 코드를 가지고 있습니다. 자세한 내용은 이것을 참조하십시오.

Console.WriteLine((int)'μ');  //956
Console.WriteLine((int)'µ');  //181

첫 번째는 다음과 같습니다.

Display     Friendly Code   Decimal Code    Hex Code    Description
====================================================================
μ           &mu;            &#956;          &#x3BC;     Lowercase Mu
µ           &micro;         &#181;          &#xB5;      micro sign Mu

영상


39

μ(mu) 및 µ(마이크로 기호) 의 특정 예의 경우 후자는 전자에 대한 호환성 분해 가 있으므로 문자열을 정규화FormKC 하거나 FormKD마이크로 기호를 mus로 변환 할 수 있습니다 .

그러나 비슷해 보이지만 유니 코드 정규화 형식에서는 동일하지 않은 문자 집합이 많이 있습니다. 예를 들어, A(Latin), Α(Greek) 및 А(Cyrillic)입니다. 유니 코드 웹 사이트에는 개발자들이 동형 이의어 공격으로부터 보호 할 수 있도록 돕기 위해 이들 목록 이 포함 된 confusables.txt 파일이 있습니다. 필요한 경우이 파일을 구문 분석하고 문자열의 "시각적 정규화"를위한 테이블을 만들 수 있습니다.


Normalize를 사용할 때 확실히 알아두면 좋습니다. 그들이 뚜렷하게 남아 있다는 것이 놀랍습니다.
user2864740 dec.

4
@ user2864740 : 그리스어 대문자 타우가 로마자 T와 구별되지 않는 경우 그리스어 및 로마자 텍스트를 알파벳 순서로 현명하게 정렬하기가 매우 어려울 것입니다. 또한 서체가 그리스 문자와 로마 문자에 대해 다른 시각적 스타일을 사용한다면 로마 문자와 모양이 비슷한 그리스 문자가 그렇지 않은 문자와 다르게 렌더링되면 매우 산만해질 것입니다.
supercat

7
더 중요한 것은 유럽 알파벳을 통합하면 구현하기가 어렵 ToUpper거나 ToLower어렵습니다. 영어로 되어 있어야 하지만 그리스어와 러시아어로 되어 있어야 "B".ToLower()합니다 . 현재 터키어 (점 없음 ) 및 다른 몇 가지 언어 만 기본값과 다른 대소 문자 규칙이 필요합니다. bβвi
dan04 dec.

@ dan04 : 터키어 "i"와 "I"의 네 가지 변형 모두에 고유 한 코드 포인트를 할당하는 것을 고려한 사람이 있는지 궁금합니다. 그것은 toUpper / toLower의 동작에서 모호성을 제거했을 것입니다.
supercat 2014-08-20

34

유니 코드 데이터베이스 에서 두 문자를 모두 검색 하고 차이점을 확인하십시오 .

하나는 그리스 소문자 µ 이고 다른 하나는 마이크로 기호 µ 입니다.

Name            : MICRO SIGN
Block           : Latin-1 Supplement
Category        : Letter, Lowercase [Ll]
Combine         : 0
BIDI            : Left-to-Right [L]
Decomposition   : <compat> GREEK SMALL LETTER MU (U+03BC)
Mirror          : N
Index entries   : MICRO SIGN
Upper case      : U+039C
Title case      : U+039C
Version         : Unicode 1.1.0 (June, 1993)

Name            : GREEK SMALL LETTER MU
Block           : Greek and Coptic
Category        : Letter, Lowercase [Ll]
Combine         : 0
BIDI            : Left-to-Right [L]
Mirror          : N
Upper case      : U+039C
Title case      : U+039C
See Also        : micro sign U+00B5
Version         : Unicode 1.1.0 (June, 1993)

4
어떻게 37 개의 찬성표를 얻었습니까? 질문 ( "유니 코드 문자를 비교하는 방법")에 대한 대답이 아니라이 특정 예제가 동일하지 않은 이유에 대해서만 설명합니다. 기껏해야 질문에 대한 의견이어야합니다. 댓글 형식 지정 옵션이 답변 형식 지정 옵션만큼 멋지게 게시하는 것을 허용하지 않는다는 것을 이해합니다. 그러나 그것이 답변으로 게시 할 타당한 이유가되어서는 안됩니다.
Konerak

5
사실 그 질문은 왜 μ와 µ 동등 검사가 거짓을 반환하는지 묻는 질문이었습니다. 이 답변이 대답합니다. 나중에 OP는 닮은 두 문자를 비교하는 방법에 대한 또 다른 질문 (이 질문)을 요청했습니다. 두 질문 모두 우수 답변이 있었고 나중에 중재자 중 한 명이 두 질문을 병합하여 두 번째 질문의 우수 답변을 우수로 선택했습니다. 누군가가 요약됩니다 그래서,이 질문을 편집
수빈 야곱에게

사실, 난 병합 후 콘텐츠를 추가하지 않은
수빈 야곱에게

24

편집 이 질문을 C #에서 'μ'와 'µ'를 비교하는 방법 과 병합 한 후
게시 된 원래 답변 :

 "μ".ToUpper().Equals("µ".ToUpper()); //This always return true.

편집 주석을 읽은 후에는 다른 유형의 입력에 대해 잘못된 결과를 제공 할 수 있기 때문에 위의 방법을 사용하는 것은 좋지 않습니다.이 때문에 위키 에서 언급 한대로 완전한 호환성 분해를 사용하여 정규화 를 사용해야합니다 . ( BoltClock이 게시 한 답변에 감사드립니다 )

    static string GREEK_SMALL_LETTER_MU = new String(new char[] { '\u03BC' });
    static string MICRO_SIGN = new String(new char[] { '\u00B5' });

    public static void Main()
    {
        string Mus = "µμ";
        string NormalizedString = null;
        int i = 0;
        do
        {
            string OriginalUnicodeString = Mus[i].ToString();
            if (OriginalUnicodeString.Equals(GREEK_SMALL_LETTER_MU))
                Console.WriteLine(" INFORMATIO ABOUT GREEK_SMALL_LETTER_MU");
            else if (OriginalUnicodeString.Equals(MICRO_SIGN))
                Console.WriteLine(" INFORMATIO ABOUT MICRO_SIGN");

            Console.WriteLine();
            ShowHexaDecimal(OriginalUnicodeString);                
            Console.WriteLine("Unicode character category " + CharUnicodeInfo.GetUnicodeCategory(Mus[i]));

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormC);
            Console.Write("Form C Normalized: ");
            ShowHexaDecimal(NormalizedString);               

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormD);
            Console.Write("Form D Normalized: ");
            ShowHexaDecimal(NormalizedString);               

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormKC);
            Console.Write("Form KC Normalized: ");
            ShowHexaDecimal(NormalizedString);                

            NormalizedString = OriginalUnicodeString.Normalize(NormalizationForm.FormKD);
            Console.Write("Form KD Normalized: ");
            ShowHexaDecimal(NormalizedString);                
            Console.WriteLine("_______________________________________________________________");
            i++;
        } while (i < 2);
        Console.ReadLine();
    }

    private static void ShowHexaDecimal(string UnicodeString)
    {
        Console.Write("Hexa-Decimal Characters of " + UnicodeString + "  are ");
        foreach (short x in UnicodeString.ToCharArray())
        {
            Console.Write("{0:X4} ", x);
        }
        Console.WriteLine();
    }

산출

INFORMATIO ABOUT MICRO_SIGN    
Hexa-Decimal Characters of µ  are 00B5
Unicode character category LowercaseLetter
Form C Normalized: Hexa-Decimal Characters of µ  are 00B5
Form D Normalized: Hexa-Decimal Characters of µ  are 00B5
Form KC Normalized: Hexa-Decimal Characters of µ  are 03BC
Form KD Normalized: Hexa-Decimal Characters of µ  are 03BC
 ________________________________________________________________
 INFORMATIO ABOUT GREEK_SMALL_LETTER_MU    
Hexa-Decimal Characters of µ  are 03BC
Unicode character category LowercaseLetter
Form C Normalized: Hexa-Decimal Characters of µ  are 03BC
Form D Normalized: Hexa-Decimal Characters of µ  are 03BC
Form KC Normalized: Hexa-Decimal Characters of µ  are 03BC
Form KD Normalized: Hexa-Decimal Characters of µ  are 03BC
 ________________________________________________________________

Unicode_equivalence로 정보를 읽는 동안 발견했습니다.

동등성 기준의 선택은 검색 결과에 영향을 줄 수 있습니다. 예를 들어 U + FB03 (ffi), ..... 그래서 U + 0066 (f)를 부분 문자열로 검색 하면 U + FB03 의 NFKC 정규화에서는 성공 하지만 U + FB03의 NFC 정규화에서는 성공 하지 못합니다 .

따라서 동등성을 비교하려면 일반적으로 FormKCNFKC 정규화 또는 NFKD 정규화를 사용해야합니다 FormKD.
모든 유니 코드 문자에 대해 더 많이 알고 싶어서별로 궁금하지 않았기 때문에 모든 유니 코드 문자를 반복하는 샘플을 만들었고 UTF-16논의하고 싶은 결과를 얻었습니다.

  • FormCFormD정규화 된 값이 동일하지 않은 문자에 대한 정보
    Total: 12,118
    Character (int value): 192-197, 199-207, 209-214, 217-221, 224-253, ..... 44032-55203
  • FormKCFormKD정규화 된 값이 동일하지 않은 문자에 대한 정보
    Total: 12,245
    Character (int value): 192-197, 199-207, 209-214, 217-221, 224-228, ..... 44032-55203, 64420-64421, 64432-64433, 64490-64507, 64512-64516, 64612-64617, 64663-64667, 64735-64736, 65153-65164, 65269-65274
  • FormCFormD정규화 된 값이 동일하지 않은 모든 문자 , there FormKCFormKD정규화 된 값도 다음 문자를 제외하고 동일하지 않습니다
    .901 '΅', 8129 '῁', 8141 '῍', 8142 '῎', 8143 '῏', 8157 '῝', 8158 '῞'
    , 8159 '῟', 8173 '῭', 8174 '΅'
  • 추가 캐릭터 그 FormKCFormKD정규화 된 값은 동등하지 않았다, 그러나 거기 FormCFormD정규화 값은 상당했다
    Total: 119
    문자 :452 'DŽ' 453 'Dž' 454 'dž' 12814 '㈎' 12815 '㈏' 12816 '㈐' 12817 '㈑' 12818 '㈒' 12819 '㈓' 12820 '㈔' 12821 '㈕', 12822 '㈖' 12823 '㈗' 12824 '㈘' 12825 '㈙' 12826 '㈚' 12827 '㈛' 12828 '㈜' 12829 '㈝' 12830 '㈞' 12910 '㉮' 12911 '㉯' 12912 '㉰' 12913 '㉱' 12914 '㉲' 12915 '㉳' 12916 '㉴' 12917 '㉵' 12918 '㉶' 12919 '㉷' 12920 '㉸' 12921 '㉹' 12922 '㉺' 12923 '㉻' 12924 '㉼' 12925 '㉽' 12926 '㉾' 13056 '㌀' 13058 '㌂' 13060 '㌄' 13063 '㌇' 13070 '㌎' 13071 '㌏' 13072 '㌐' 13073 '㌑' 13075 '㌓' 13077 '㌕' 13080 '㌘' 13081 '㌙' 13082 '㌚' 13086 '㌞' 13089 '㌡' 13092 '㌤' 13093 '㌥' 13094 '㌦' 13099 '㌫' 13100 '㌬' 13101 '㌭' 13102 '㌮' 13103 '㌯' 13104 '㌰' 13105 '㌱' 13106 '㌲' 13108 '㌴' 13111 '㌷' 13112 '㌸' 13114 '㌺' 13115 '㌻' 13116 '㌼' 13117 '㌽' 13118 '㌾' 13120 '㍀' 13130 '㍊' 13131 '㍋' 13132 '㍌' 13134 '㍎' 13139 '㍓' 13140 '㍔' 13142 '㍖' .......... ﺋ' 65164 'ﺌ' 65269 'ﻵ' 65270 'ﻶ' 65271 'ﻷ' 65272 'ﻸ' 65273 'ﻹ' 65274'
  • 정규화 할 수없는 문자가 있습니다.ArgumentException 시도하면 던집니다.
    Total:2081 Characters(int value): 55296-57343, 64976-65007, 65534

이 링크는 유니 코드 동등성에 적용되는 규칙을 이해하는 데 매우 유용 할 수 있습니다.

  1. Unicode_equivalence
  2. Unicode_compatibility_characters

4
이상하지만 작동합니다 ... 내 말은 서로 다른 의미를 가진 두 개의 다른 문자이며 그들을 대문자로 변환하면 동등하게 만들 수 있습니까? 나는 논리하지만 좋은 솔루션 1 참조 해달라고
BudBrot

45
이 솔루션은 문제를 숨기고 일반적인 경우 문제를 일으킬 수 있습니다. 테스트 이런 종류의 것을 찾을 것이라고 "m".ToUpper().Equals("µ".ToUpper());하고 "M".ToUpper().Equals("µ".ToUpper());또한 사실이다. 이것은 바람직하지 않을 수 있습니다.
Andrew Leach

6
-1 – 이것은 끔찍한 생각입니다. 이와 같이 유니 코드로 작업하지 마십시오.
Konrad Rudolph

1
ToUpper () 기반 트릭 대신 String.Equals ( "μ", "μ", StringComparison.CurrentCultureIgnoreCase)를 사용하지 않는 이유는 무엇입니까?
svenv

6
"마이크로 사인"과 "그리스 소문자 뮤"를 구별하는 한 가지 좋은 이유가 있습니다. 마이크로 사인의 "대문자"가 여전히 마이크로 사인이라고 말하는 것입니다. 그러나 대문자는 마이크로에서 메가의 행복한 엔지니어링으로 바뀝니다.
그렉

9

대부분의 경우 동일한 문자를 (가시적으로) 만드는 두 가지 다른 문자 코드가 있습니다. 기술적으로 동일하지는 않지만 동일하게 보입니다. 캐릭터 테이블을보고 해당 캐릭터의 여러 인스턴스가 있는지 확인하십시오. 또는 코드에있는 두 문자의 문자 코드를 인쇄하십시오.


6

"비교하는 방법"을 묻지 만하고 싶은 일을 말하지 않습니다.

이들을 비교하는 데에는 최소한 두 가지 주요 방법이 있습니다.

당신이있는 그대로 직접 비교하거나 그들은 다릅니다

또는 일치 할 항목을 찾는 비교가 필요한 경우 유니 코드 호환성 정규화를 사용합니다.

하지만 유니 코드 호환성 정규화로 인해 다른 많은 문자가 동일하게 비교되기 때문에 문제가있을 수 있습니다. 이 두 문자 만 똑같이 취급하려면 자체 정규화 또는 비교 함수를 실행해야합니다.

보다 구체적인 솔루션을 위해서는 귀하의 특정 문제를 알아야합니다. 이 문제를 접하게 된 배경은 무엇입니까?


1
"마이크로 기호"와 소문자 mu 문자가 정규적으로 동일합니까? 정규 정규화를 사용하면보다 엄격한 비교가 가능합니다.
Tanner Swett

TannerL.Swett @ : 사실은 내가 ... 심지어 있는지 내 머리 위로 떨어져 그것을 확인하는 방법을 모르겠어요
hippietrail

1
사실 저는 물리 공식이있는 파일을 가져 왔습니다. 당신은 정규화에 대해 옳습니다. 좀 더 깊게 봐야
DJ

어떤 종류의 파일? 사람이 일반 유니 코드 텍스트로 손으로 만든 것? 아니면 앱에서 특정 형식으로 출력 한 것이 있습니까?
hippietrail

5

내가 현학적 인 사람이되고 싶다면 당신의 질문이 말이 안된다고 말하고 싶지만, 우리가 크리스마스가 다가오고 있고 새들이 노래하고 있기 때문에 나는 이것을 진행할 것입니다.

첫째로, 당신은 비교하려고하는이 개 단체는 glyphs는 그래프는 일반적으로 "폰트"로 알고 무엇에 의해 제공 상형 문자의 집합, 보통에서 오는 것의 일부이며 ttf, otf어떤 파일 형식 당신이 나 사용.

글리프는 주어진 기호의 표현이며, 특정 세트에 의존하는 표현이기 때문에 2 개의 유사하거나 "더 나은"동일한 기호를 기대할 수 없으며 이해가되지 않는 구문입니다. 문맥을 고려한다면 적어도 이와 같은 질문을 만들 때 고려할 글꼴이나 글리프 세트를 지정해야합니다.

일반적으로 발생하는 문제와 유사한 문제를 해결하는 데 사용되는 것은 OCR입니다. 본질적으로 글리프를 인식하고 비교하는 소프트웨어입니다. C # 이 기본적으로 OCR 을 제공 하는지 모르겠지만 일반적으로 정말 나쁩니다. OCR이 정말로 필요하지 않고 어떻게해야할지 알고 있는지 생각해보십시오.

OCR이 일반적으로 리소스 측면에서 비싸다는 사실을 언급하지 않고 물리학 책을 고대 그리스 책으로 해석 할 수 있습니다.

이러한 문자가 현지화 된 방식으로 현지화되는 데에는 이유가 있습니다. 그렇게하지 마십시오.


1

DrawString메서드를 사용하여 동일한 글꼴 스타일과 크기로 두 문자를 모두 그릴 수 있습니다 . 심볼이있는 두 개의 비트 맵이 생성 된 후 픽셀 단위로 비교할 수 있습니다.

이 방법의 장점은 절대 동일 문자뿐만 아니라 유사한 문자도 비교할 수 있다는 것입니다 (확실한 허용 오차 포함).

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