2009-11-03 3 views
15

J'ai été chargé de générer tous les caractères dans le jeu de caractères UTF-8 pour tester comment un système gère chacun d'entre eux. Je n'ai pas beaucoup d'expérience avec l'encodage de caractères. L'approche que j'allais essayer était d'incrémenter un compteur, puis d'essayer de traduire ce nombre base dix en son équivalent UTF-8, mais jusqu'à présent je n'ai pas réussi à trouver un moyen efficace de le faire en C# 3.5Comment générer tous les caractères dans le jeu de caractères UTF-8 dans .net

Toutes les suggestions seraient grandement appréciées.

+1

Il y a 1 112 064 points de code dans la norme Unicode. Voulez-vous vraiment les générer tous? –

+1

Merci pour les commentaires, cela clarifie beaucoup les choses. Pour mieux expliquer ma tâche, je teste un service qui semble étouffer certains caractères chinois. Il a donc été décidé que nous devrions pouvoir tester tous les caractères étrangers, car le système devra être capable de gérer une variété de langues internationales. Je suis à la recherche d'une solution simple et efficace qui ne tarde pas à créer et qui a une couverture maximale. – FireWire

Répondre

2

UTF-8 est pas un caractère défini - il est un caractère codage qui est capable de coder tous les caractères dans le jeu de caractères Unicode en données binaires.

Pourriez-vous plus d'informations sur ce que vous essayez de faire? Vous pouvez encoder tous les caractères Unicode possibles (y compris ceux qui ne sont pas alloués pour le moment) mais si vous devez gérer des caractères en dehors du plan multilingue de base (ie au dessus de U + FFFF), cela devient un peu plus compliqué ...

1

UTF-8 n'est pas un charset, c'est un encodage. Toute valeur dans Unicode peut être codée en UTF-8 avec différentes longueurs d'octets.

Pour .net, les personnages sont 16 bits (ce n'est pas l'ensemble complet de unicode, mais est le plus pratique), vous pouvez essayer ceci:

for (char i = 0; i < 65536; i++) { 
    string s = "" + i; 
    byte[] bytes = Encoding.UTF8.GetBytes(s); 
    // do something with bytes 
} 
+2

Votre code est correct, mais votre deuxième paragraphe est trompeur. 'System.Char' est une valeur de 16 bits, true. Mais MSDN indique clairement qu'un 'System.Char' est un point de code UTF-16, ce qui signifie qu'il ne s'agit pas techniquement d'un caractère. Il y a beaucoup de caractères Unicode qui peuvent être représentés en UTF-8 avec des points de code au dessus de 65536. Vous dites "ce n'est pas l'ensemble complet d'Unicode c'est le plus pratique" - je ne suis pas certain que c'est vrai, et c'est certainement pas une bonne raison d'éviter de tester les points de code au-dessus de U + FFFF. –

6

Il n'y a pas de « caractères UTF-8 ". Voulez-vous dire les caractères Unicode ou l'encodage UTF-8 des caractères Unicode?

Il est facile de convertir un int en un caractère Unicode, à condition bien sûr qu'il ya une correspondance pour ce code:

char c = (char)theNumber; 

Si vous voulez l'encodage UTF-8 pour ce caractère, qui est pas très difficile soit:

byte[] encoded = Encoding.UTF8.GetBytes(c.ToString()) 

Vous devez vérifier la norme Unicode pour voir les plages de numéros où des caractères Unicode sont définis.

5

Même lorsque vous générez tous les caractères, vous trouverez que ce n'est pas un test efficace. Certains des caractères sont combinant marques, ce qui signifie qu'ils vont se combiner avec le prochain personnage à venir après eux - avoir une chaîne pleine de marques de combinaison n'aura pas beaucoup de sens. Il y a d'autres cas spéciaux aussi. Il vaudra beaucoup mieux utiliser le texte dans les langues que vous devez prendre en charge.

6
System.Net.WebClient client = new System.Net.WebClient(); 
string definedCodePoints = client.DownloadString(
         "http://unicode.org/Public/UNIDATA/UnicodeData.txt"); 
System.IO.StringReader reader = new System.IO.StringReader(definedCodePoints); 
System.Text.UTF8Encoding encoder = new System.Text.UTF8Encoding(); 
while(true) { 
    string line = reader.ReadLine(); 
    if(line == null) break; 
    int codePoint = Convert.ToInt32(line.Substring(0, line.IndexOf(";")), 16); 
    if(codePoint >= 0xD800 && codePoint <= 0xDFFF) { 
    //surrogate boundary; not valid codePoint, but listed in the document 
    } else { 
    string utf16 = char.ConvertFromUtf32(codePoint); 
    byte[] utf8 = encoder.GetBytes(utf16); 
    //TODO: something with the UTF-8-encoded character 
    } 
} 

Le code ci-dessus doit parcourir les caractères Unicode actuellement attribués. Vous voudrez probablement analyser le fichier UnicodeData localement et corriger les erreurs C# que j'ai faites.

L'ensemble des caractères Unicode actuellement attribués est inférieur à l'ensemble qui pourrait être défini.Bien sûr, le fait que vous voyiez un personnage lorsque vous en imprimez un dépend de beaucoup d'autres facteurs, comme les polices et les autres applications qu'il transmettra avant qu'il ne soit émis dans votre globe oculaire.

1

Cela vous donnera tous les personnages dans un jeu de caractères - assurez-vous de spécifier un jeu de caractères lors de la spécification du codage:

var results = new ConcurrentBag<int>(); 
Parallel.For (0, 10, set => { 
    var encoding = Encoding.GetEncoding ("ISO-8859-1"); 
    var c = encoding.GetEncoder(); 
    c.Fallback = new EncoderExceptionFallback(); 
    var start = set * 1000; 
    var end = start + 1000; 
    Console.WriteLine ("Worker #{0}: {1} - {2}", set, start, end); 

    char[] input = new char[1]; 
    byte[] output = new byte[5]; 
    for (int i = start; i < end; i++) { 
     try { 
      input[0] = (char)i; 
      c.GetBytes (input, 0, 1, output, 0, true); 
      results.Add (i); 
     } 
     catch { 
     } 
    } 
}); 
var hashSet = new HashSet<int> (results); 
//hashSet.Remove ((int)'\r'); 
//hashSet.Remove ((int)'\n'); 
var sorted = hashSet.ToArray(); 
Array.Sort (sorted); 
var charset = new string (sorted.Select (i => (char)i).ToArray()); 
0

Ce code produira la sortie dans un fichier. Tous les caractères imprimables ou non seront là.

Encoding enc = (Encoding)Encoding.GetEncoding("utf-8").Clone(); 
enc.EncoderFallback = new EncoderReplacementFallback(""); 
char[] chars = new char[1]; 
byte[] bytes = new byte[16]; 

using (StreamWriter sw = new StreamWriter(@"C:\utf-8.txt")) 
{ 
    for (int i = 0; i <= char.MaxValue; i++) 
    { 
     chars[0] = (char)i; 
     int count = enc.GetBytes(chars, 0, 1, bytes, 0); 

     if (count != 0) 
     { 
      sw.WriteLine(chars[0]); 
     } 
    } 
} 
1

Vous pouvez-force brute un Encoding à déterminer quel code indique qu'il prend en charge. Pour ce faire, il suffit de parcourir tous les points de code possibles, de les convertir en chaînes et de voir si Encoding.GetBytes() déclenche une exception ou non (après avoir défini Encoding.EncoderFallback à EncoderExceptionFallback).

IEnumerable<int> GetAllWritableCodepoints(Encoding encoding) 
{ 
    encoding = Encoding.GetEncoding(encoding.WebName, new EncoderExceptionFallback(), new DecoderExceptionFallback()); 

    var i = -1; 
    // Docs for char.ConvertFromUtf32() say that 0x10ffff is the maximum code point value. 
    while (i != 0x10ffff) 
    { 
     i++; 

     var success = false; 
     try 
     { 
      encoding.GetByteCount(char.ConvertFromUtf32(i)); 
      success = true; 
     } 
     catch (ArgumentException) 
     { 
     } 
     if (success) 
     { 
      yield return i; 
     } 
    } 
} 

Cette méthode doit supporter des caractères représentés par la découverte de paires de substitution de Char en .net. Cependant, il est très lent (prend quelques minutes à courir sur ma machine) et probablement impraticable.

Questions connexes