2010-03-20 2 views
3

전 세계 문자를 포함하는 파일을 찾고 선택적으로 이름을 바꾸기 위해 작은 콘솔 응용 프로그램 (아래 소스)을 작성했습니다. 대부분의 소스 제어 시스템 이것에 대한 약간의 배경). 내가 사용하고있는 코드는 문자를 가진 간단한 사전을 찾아서 바꾸고 (그리고 1 바이트 이상의 저장소를 사용하는 다른 모든 문자를 핵으로 만들지 만) 매우 익숙한 느낌이 들게됩니다. (a) 캐릭터가 국제적인지 여부를 알아내는 올바른 방법은 무엇입니까? (b) 최고의 ASCII 대체 문자는 무엇입니까?C#을 사용하여 파일 이름 문자가 국제적인 것으로 간주되는지 확인합니다.

왜 이것이 필요한지에 대한 배경 정보를 제공해 드리겠습니다. 덴마크의 A 문자에는 UTF-8로 된 두 개의 다른 인코딩이 있는데, 둘 다 동일한 기호를 나타냅니다. NFC 및 NFD 인코딩으로 알려져 있습니다. Windows와 Linux는 기본적으로 NFC 인코딩을 만들지 만 지정된 인코딩을 존중합니다. Mac은 모든 이름 (HFS + 파티션에 저장할 때)을 NFD로 변환하므로 Windows에서 작성된 파일의 이름에 대해 다른 바이트 스트림을 반환합니다. 이것은 Subversion, Git 및이 시나리오를 적절하게 다루지 않는 다른 많은 유틸리티를 효과적으로 파기합니다.

현재 Mercurial을 평가 중입니다. 국제 문자를 처리 할 때 더 나빠질 수 있습니다. 이러한 문제에 상당히 지쳐서 소스 제어 또는 국제 문자가 있어야하고 여기에 우리가 있습니다.

내 현재의 구현 :

public class Checker 
{ 
    private Dictionary<char, string> internationals = new Dictionary<char, string>(); 
    private List<char> keep = new List<char>(); 
    private List<char> seen = new List<char>(); 

    public Checker() 
    { 
     internationals.Add('æ', "ae"); 
     internationals.Add('ø', "oe"); 
     internationals.Add('å', "aa"); 
     internationals.Add('Æ', "Ae"); 
     internationals.Add('Ø', "Oe"); 
     internationals.Add('Å', "Aa"); 

     internationals.Add('ö', "o"); 
     internationals.Add('ü', "u"); 
     internationals.Add('ä', "a"); 
     internationals.Add('é', "e"); 
     internationals.Add('è', "e"); 
     internationals.Add('ê', "e"); 

     internationals.Add('¦', ""); 
     internationals.Add('Ã', ""); 
     internationals.Add('©', ""); 
     internationals.Add(' ', ""); 
     internationals.Add('§', ""); 
     internationals.Add('¡', ""); 
     internationals.Add('³', ""); 
     internationals.Add('­', ""); 
     internationals.Add('º', ""); 

     internationals.Add('«', "-"); 
     internationals.Add('»', "-"); 
     internationals.Add('´', "'"); 
     internationals.Add('`', "'"); 
     internationals.Add('"', "'"); 
     internationals.Add(Encoding.UTF8.GetString(new byte[] { 226, 128, 147 })[ 0 ], "-"); 
     internationals.Add(Encoding.UTF8.GetString(new byte[] { 226, 128, 148 })[ 0 ], "-"); 
     internationals.Add(Encoding.UTF8.GetString(new byte[] { 226, 128, 153 })[ 0 ], "'"); 
     internationals.Add(Encoding.UTF8.GetString(new byte[] { 226, 128, 166 })[ 0 ], "."); 

     keep.Add('-'); 
     keep.Add('='); 
     keep.Add('\''); 
     keep.Add('.'); 
    } 

    public bool IsInternationalCharacter(char c) 
    { 
     var s = c.ToString(); 
     byte[] bytes = Encoding.UTF8.GetBytes(s); 
     if(bytes.Length > 1 && ! internationals.ContainsKey(c) && ! seen.Contains(c)) 
     { 
      Console.WriteLine("X '{0}' ({1})", c, string.Join(",", bytes)); 
      seen.Add(c); 
      if(! keep.Contains(c)) 
      { 
       internationals[ c ] = ""; 
      } 
     } 
     return internationals.ContainsKey(c); 
    } 

    public bool HasInternationalCharactersInName(string name, out string safeName) 
    { 
     StringBuilder sb = new StringBuilder(); 
     Array.ForEach(name.ToCharArray(), c => sb.Append(IsInternationalCharacter(c) ? internationals[ c ] : c.ToString())); 
     int length = sb.Length; 
     sb.Replace(" ", " "); 
     while(sb.Length != length) 
     { 
      sb.Replace(" ", " "); 
     } 
     safeName = sb.ToString().Trim(); 
     string namePart = Path.GetFileNameWithoutExtension(safeName); 
     if(namePart.EndsWith(".")) 
      safeName = namePart.Substring(0, namePart.Length - 1) + Path.GetExtension(safeName); 
     return name != safeName; 
    } 
} 

그리고 이것은 다음과 같이 호출 할 것이다 :

FileInfo file = new File("Århus.txt"); 
string safeName;  
if(checker.HasInternationalCharactersInName(file.Name, out safeName)) 
{ 
    // rename file 
} 
+1

주 작은 따옴표 콘솔 창에서 Visual Studio로 복사하여 Chrome을 통해 StackOverflow에 복사 할 수 있다는 사실에 놀라움을 금치 못했습니다. 그러나 우리가 내용보다는 파일 이름을 말하자마자 우리는 1980 년대에 다시 돌아 왔습니다. –

답변

1

이 시대에 슬픈 문제가 있습니다. MAC이 사용하는 NFD 형식이 분명히이 두통을 유발합니다. 당신이 고려할 수있는 한 가지는 NFD가 NFC와 다른 원인이되는 글리프 (glyphs)에서 분음 기호를 제거하는 것입니다.

나는이 (특히 아시아 스크립트) 정확 100 % 확실하지 않다,하지만 가까이한다고 ""에 ''에서 매핑이 실제로 사이의 문자를 포함

public static string RemoveDiacriticals(string txt) { 
    string nfd = txt.Normalize(NormalizationForm.FormD); 
    StringBuilder retval = new StringBuilder(nfd.Length); 
    foreach (char ch in nfd) { 
    if (ch >= '\u0300' && ch <= '\u036f') continue; 
    if (ch >= '\u1dc0' && ch <= '\u1de6') continue; 
    if (ch >= '\ufe20' && ch <= '\ufe26') continue; 
    if (ch >= '\u20d0' && ch <= '\u20f0') continue; 
    retval.Append(ch); 
    } 
    return retval.ToString(); 
} 
+0

이것은 내가 찾고있는 것처럼 보입니다. 나는 다른 규범으로 문자열을 정규화하고 결과를 비교하는 접근법에 대해 생각할 것입니다. 이것은 dan04의 대답과 결합하여 퍼즐의 첫 번째 부분을 해결해야합니다. 여전히 최상의 ASCII 대체 문자가 무엇인지 파악해야하며, 테이블 또는 사전이 필요없는 코드 솔루션을 사용하는 것이 좋습니다. 표시 할 업데이트 된 코드가 있으면 새로운 질문을 게시 할 것입니다. –

2

의 (a) 단일. 127보다 큰 코드 포인트가 있는지 확인하십시오.

(b) NKFD 정규화 및/또는 uni2ascii을 시도하십시오. 당신은 무력을 신경 쓰지 않는 경우

+0

코드 포인트는 어느 바이트입니까? 나는 이것을 조사 할 수 있었다. 그러나 만일 당신이 알고 있으면 나는 암시를 바르게 평가할 것이다. C 소스가 제공되기 때문에 uni2ascii 유틸리티는 Windows에서 사용할 수있는 것 같지 않습니다. 정규화를 직접 구현하여 휠을 발명 할 필요가 없다면 C# 라이브러리 나 Windows API가 없습니까? –

+0

유니 코드 코드 포인트는 21 비트 숫자입니다. 이것은 UTF-8, 1-2 UTF-16 코드 단위 또는 1 UTF-32 코드 단위로 1-4 바이트로 인코딩 될 수 있습니다. 이 세 가지 모두 ASCII 문자의 경우 0-127 범위의 단일 코드 단위를 사용합니다. Windows API에는 NormalizeString이라는 함수가 있습니다. – dan04

+0

감사합니다. 나는 이것을 파헤쳐 볼 것입니다. –

1

,이 같은 시도 할 수 있습니다 :

string name = "Århus.txt"; 
string kd = name.Normalize(NormalizationForm.FormKD); 
byte[] kd_bytes = Encoding.Unicode.GetBytes(kd); 
byte[] ascii_bytes = Encoding.Convert(Encoding.Unicode, Encoding.ASCII, kd_bytes); 
string flattened = Encoding.ASCII.GetString(ascii_bytes); 

이가 Århus.txt 변환됩니다 rhus.txt의 KD 양식이 떨어져 Å을 나누기 때문에 7 비트 ASCII 로의 변환은 분음 기호를 잃어 버린다. 작은 것을 어떻게 처리해야할까요?

귀하의 마일리지는 다른 문자에 따라 다를 수 있지만 KD 정규화가이 트릭을 수행해야한다고 생각합니다. 몇 년 동안 코드 페이지 변환 작업을하지 않았지만 흥미로운 질문을 발견했습니다.

편집 :

난 그냥 æÆØ을 시도하고 그들은 모두로 변환, 그래서 이것은 당신을 위해 너무 손실이 될 수있다?. 그래도 대답을 유도하는 데 필요한 단서를 줄 수 있습니다.

+0

감사합니다.이 방법을 실험 해 보겠습니다. –

관련 문제