ファイル名の文字が国際的と見なされているかどうかを検出するために、C#を使用しました
-
21-09-2019 - |
質問
私は、彼らはほとんどのソース管理システム(以下、この上のいくつかの背景)と一定の痛みの源であるとして、検索し、必要に応じて国際文字を含むファイルの名前を変更するために、小さなコンソールアプリケーション(以下ソース)を書きました。私が使用しているコードは(用途はより多くのストレージの1バイト以外ことを他のすべての文字をし、核兵器)を探して交換する文字と簡単な辞書を持っていますが、それは非常にハック感じています。 (a)の文字が国際的であるかどうかを調べるために正しい方法は何ですか?および(b)は、最良のASCII置換文字は何でしょうか?
私はこれが必要な理由について、いくつかの背景情報を提供してみましょう。とてもデンマークÅの文字がUTF-8に二つの異なるエンコーディングを持っていることが起こる、どちらも同じシンボルを表します。これらは、NFCとNFDエンコーディングとして知られています。 WindowsとLinuxは、それが与えられているものは何でもエンコード既定が、尊敬でエンコードするNFCを作成します。マックは(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
}
解決
悲しい問題は、この日および年齢で持っています。明らかにMACが使用するNFDフォームはあなたにこの頭痛を引き起こしています。あなたが考える可能性の一つは、NFDは、NFCとは異なることになりグリフから特殊記号を削除されます。
私は確かに、これは(特にアジアのスクリプトのために)完全に正確である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();
}
他のヒント
()シンプル。 127より大きい任意のコードポイントをチェックします。
(B)NKFD正規化を試し、および/または uni2ascii の
あなたはブルートフォースを気にしない場合、あなたはこのような何かを試すことができます:
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);
KDフォーム休憩離れÅ、および7ビットのASCIIへの変換は、ダイアクリティカルマークを失うので、これは、A?rhus.txtにÅrhus.txtを変換します。少しで何をする?」あなたに残さアップですね。
あなたの走行距離は、他の文字に異なる場合がありますが、私はトリックを行う必要がKDの正規化を推測します。私は今、年間のコード・ページ変換に取り組んでいないが、私は興味深い質問を見つけます。
EDITます:
私はちょうどæÆØを試してみましたが、これはあなたのためにあまりにも非可逆かもしれので、それらはすべて、?に変換しました。それでも、それはあなたにリードその答えにいくつかの手がかりを与える可能性があります。