Frage

Ich habe eine kleine Konsolenanwendung (Quelle unten) geschrieben zu lokalisieren und ggf. Umbenennen von Dateien internationale Zeichen enthalten, da sie eine Quelle des ständigen Schmerzen bei den meisten Versionskontrollsystemen (einiger Hintergrund dazu weiter unten) ist. Der Code, den ich verwende, hat ein einfaches Wörterbuch mit Zeichen zu suchen und ersetzen (und Nukes jedes anderes Zeichen, das verwendet mehr als ein Byte Speicherplatz), aber es fühlt sich sehr hackish. Was ist der richtige Weg, um (a) herausfinden, ob ein Zeichen ist international? und (b), was der beste ASCII Substitution Charakter wäre?

Lassen Sie uns bieten einige Hintergrundinformationen darüber, warum dies notwendig ist. Nun ist es so, dass der dänische ein Charakter hat zwei verschiedene Codierungen in UTF-8, die beide das gleiche Symbol darstellen. Diese werden als NFC und NFD Kodierungen bekannt. Windows und Linux wird erstellen NFC kodieren standardmäßig aber Achtung, was die Kodierung es gegeben ist. Mac konvertiert alle Namen NFD (wenn sie auf eine HFS + Partition zu speichern) und daher gibt einen anderen Byte-Strom für den Namen einer Datei unter Windows erstellt. Diese effektiv bricht Subversion, Git und viele andere Dienstprogramme, die kümmern sich nicht um dieses Szenario ordnungsgemäß zu behandeln.

Ich bin derzeit die Bewertung Mercurial, die im Umgang mit internationalen Zeichen noch schlimmer zu sein, stellt sich heraus, .. ist ziemlich müde von diesen Problemen, entweder Quellensteuerung oder der internationale Charakter würde gehen müssen, und so sind wir hier.

Meine aktuelle Implementierung:

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;
    }
}

Und dies würde wie folgt aufgerufen werden:

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

Lösung

Sad Problem in der heutigen Zeit zu haben. Klar, dass die NFD Form, dass die MAC-Anwendungen Sie diese Kopfschmerzen verursacht. Eine Sache, die Sie in Erwägung ziehen könnte, um die diakritischen Zeichen aus den Glyphen zu entfernen, die NFD verursacht von NFC, anders zu sein.

Ich bin nicht 100% sicher, dass dies völlig korrekt ist (vor allem für asiatische Schriftzeichen), aber es sollte nahe sein:

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();
}

Andere Tipps

(a) einfach. Prüfen Sie, ob Codepunkte, die größer als 127.

(b) Versuchen NKFD Normalisierung und / oder uni2ascii .

Wenn Sie rohe Gewalt nichts ausmacht, können Sie so etwas wie dies versuchen:

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);

Dies wird konvertieren Århus.txt auf A? Rhus.txt, weil die KD Form bricht die Å voneinander entfernt, und die Umwandlung in 7-bit ASCII das diakritische Zeichen verliert. Was mit dem wenig zu tun? "Über dir liegt s links.

Ihre Laufleistung auf den anderen Charakteren kann variieren, aber ich würde die Normalisierung KD erraten sollte es tun. Ich habe jetzt nicht auf Codepage Konvertierungen seit Jahren gearbeitet, aber ich fand die Frage interessant.

EDIT:

Ich æÆØ gerade versucht, und sie alle umzuwandelnden?, So dass diese auch verlustbehaftete für Sie sein kann. Dennoch kann es einige Hinweise darauf, dass führen zu einer Antwort geben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top