web-dev-qa-db-fra.com

Stockage d'une chaîne au format UTF8 en C #

Je fais beaucoup de manipulation de chaînes en C # et j'ai vraiment besoin que les chaînes soient stockées un octet par caractère. En effet, j'ai besoin de gigaoctets de texte simultanément en mémoire et cela cause des problèmes de mémoire faible. Je sais avec certitude que ce texte ne contiendra jamais de caractères non ASCII, donc pour mes besoins, le fait que System.String et System.Char stockent tout en deux octets par caractère est à la fois inutile et un vrai problème.

Je suis sur le point de commencer à coder mes propres classes CharAscii et StringAscii - la chaîne contiendra essentiellement ses données sous forme d'octet [] et exposera des méthodes de manipulation de chaînes similaires à celles utilisées par System.String. Cependant, cela semble beaucoup de travail pour faire quelque chose qui semble être un problème très standard, donc je poste vraiment ici pour vérifier qu'il n'y a pas déjà une solution plus facile. Existe-t-il par exemple un moyen de faire en sorte que System.String stocke en interne des données au format UTF8 que je n'ai pas remarquées, ou un autre moyen de contourner le problème?

36
PhantomDrummer

Comme vous l'avez trouvé, le CLR utilise UTF-16 pour l'encodage des caractères. Votre meilleur pari peut être d'utiliser les classes d'encodage et un BitConverter pour gérer le texte. Cette question contient de bons exemples de conversion entre les deux encodages:

Convertir une chaîne (UTF-16) en UTF-8 en C #

6
Chris

Eh bien, vous pouvez créer un wrapper qui récupère les données sous forme d'octets UTF-8 et convertit les morceaux selon les besoins en System.String, puis vice-versa pour repousser la chaîne en mémoire. La classe Encoding vous aidera ici:

var utf8 = Encoding.UTF8;
byte[] utfBytes = utf8.GetBytes(myString);

var myReturnedString = utf8.GetString(utfBytes);
11
KeithS

Pas vraiment. System.String est conçu pour stocker des chaînes. Vous avez besoin d'un sous-ensemble très particulier de chaînes avec des avantages de mémoire particuliers.

Maintenant, "un sous-ensemble très particulier de chaînes avec des avantages de mémoire particuliers" revient beaucoup, mais pas toujours le même sous-ensemble très particulier. Le code qui est uniquement ASCII n'est pas destiné à la lecture par les êtres humains, il a donc tendance à être soit des codes courts, soit quelque chose qui peut être géré de manière à traiter le flux, ou bien des morceaux de texte fusionnés avec des octets effectuant d'autres tâches ( par exemple, plusieurs formats binaires auront de petits bits qui se traduisent directement en ASCII).

En tant que tel, vous avez une exigence assez étrange.

D'autant plus lorsque vous arrivez à la partie gigaoctets. Si j'ai affaire à des concerts, je réfléchis immédiatement à la façon dont je peux cesser d'avoir à faire face à des concerts et/ou réaliser des économies beaucoup plus sérieuses que seulement 50%. Je penserais à mapper des morceaux qui ne m'intéressent pas actuellement à un fichier, ni à des cordes, ni à un tas d'autres choses. Bien sûr, cela va fonctionner pour certains cas et pas pour tous, donc encore une fois, nous ne parlons pas de quelque chose où .NET devrait coller quelque chose comme une taille unique, car une taille ne conviendra pas tout.

Au-delà de cela, juste le bit utf-8 n'est pas si difficile. Ce sont toutes les autres méthodes qui deviennent un travail. Encore une fois, ce dont vous avez besoin ne sera pas le même que quelqu'un d'autre.

2
Jon Hanna

Comme je peux voir votre problème, c'est que le caractère en C # occupe 2 octets, au lieu d'un.

Une façon de lire un fichier texte est de l'ouvrir avec:

    System.IO.FileStream fs = new System.IO.FileStream(file, System.IO.FileMode.Open);
    System.IO.BinaryReader br = new System.IO.BinaryReader(fs);

    byte[] buffer = new byte[1024];
    int read = br.Read(buffer, 0, (int)fs.Length);

    br.Close();
    fs.Close(); 

Et de cette façon, vous lisez les octets du fichier. Je l'ai essayé avec des fichiers * .txt encodés en TF-8 soit 2 octets par caractère, et ANSI soit 1 octet par caractère.

1
Thanatos