Existe-t-il un moyen de déterminer le codage d'une chaîne en C #?
Dites, j'ai une chaîne de nom de fichier, mais je ne sais pas si elle est encodée en Unicode UTF-16 ou l'encodage par défaut du système, comment puis-je le savoir?
Vous ne pouvez pas «encoder» en Unicode. Et il n'y a aucun moyen de déterminer automatiquement le codage d' une chaîne donnée, sans aucune autre information préalable.
Nicolas Dumazet
5
pour être plus clair peut-être: vous encodez des points de code Unicode en chaînes d'octets d'un jeu de caractères en utilisant un schéma de "codage" (utf- , iso- , big5, shift-jis, etc ...), et vous décodez des chaînes d'octets à partir d'un jeu de caractères sur Unicode. Vous n'encodez pas les chaînes d'octets en Unicode. Vous ne décodez pas l'Unicode en bytestrings.
Nicolas Dumazet
13
@NicDunZ - l'encodage lui-même (en particulier UTF-16) est aussi communément appelé "Unicode". Vrai ou faux, c'est la vie. Même dans .NET, regardez Encoding.Unicode - ce qui signifie UTF-16.
Marc Gravell
2
eh bien, je ne savais pas que .NET était si trompeur. Cela ressemble à une terrible habitude à apprendre. Et désolé @krebstar, ce n'était pas mon intention (je pense toujours que votre question éditée a beaucoup plus de sens maintenant qu'avant)
Nicolas Dumazet
1
@Nicdumz # 1: Il existe un moyen de déterminer de manière probabiliste quel encodage utiliser. Regardez ce que fait IE (et maintenant aussi FF avec View - Encodage de caractères - Détection automatique) pour cela: il essaie un encodage et voit s'il est peut-être "bien écrit <mettez un nom de langue ici>" ou changez-le et réessaye . Allez, ça peut être amusant!
Remarque: comme déjà indiqué, "déterminer l'encodage" n'a de sens que pour les flux d'octets. Si vous avez une chaîne, elle est déjà encodée par quelqu'un en cours de route qui connaissait déjà ou a deviné l'encodage pour obtenir la chaîne en premier lieu.
Si la chaîne est un décodage incorrect effectué avec un encodage simplement 8 bits et que l'encodage est utilisé pour le décoder, vous pouvez généralement récupérer les octets sans aucune corruption.
Nyerguds
57
Le code ci-dessous présente les caractéristiques suivantes:
Détection ou tentative de détection de UTF-7, UTF-8/16/32 (bom, no bom, little & big endian)
Retombe à la page de codes par défaut locale si aucun encodage Unicode n'a été trouvé.
Détecte (avec une forte probabilité) les fichiers Unicode avec la nomenclature / signature manquante
Recherche charset = xyz et encoding = xyz dans le fichier pour aider à déterminer l'encodage.
Pour enregistrer le traitement, vous pouvez «goûter» le fichier (nombre d'octets définissable).
Le fichier texte de codage et décodé est renvoyé.
Solution purement basée sur l'octet pour l'efficacité
Comme d'autres l'ont dit, aucune solution ne peut être parfaite (et on ne peut certainement pas faire la différence entre les différents encodages ASCII étendus 8 bits utilisés dans le monde), mais nous pouvons être `` assez bons '' surtout si le développeur présente également à l'utilisateur une liste d'encodages alternatifs comme indiqué ici: Quel est l'encodage le plus courant de chaque langue?
Une liste complète des encodages peut être trouvée en utilisant Encoding.GetEncodings();
// Function to detect the encoding for UTF-7, UTF-8/16/32 (bom, no bom, little// & big endian), and local default codepage, and potentially other codepages.// 'taster' = number of bytes to check of the file (to save processing). Higher// value is slower, but more reliable (especially UTF-8 with special characters// later on may appear to be ASCII initially). If taster = 0, then taster// becomes the length of the file (for maximum reliability). 'text' is simply// the string with the discovered encoding applied to the file.publicEncoding detectTextEncoding(string filename,outString text,int taster =1000){byte[] b =File.ReadAllBytes(filename);//////////////// First check the low hanging fruit by checking if a//////////////// BOM/signature exists (sourced from http://www.unicode.org/faq/utf_bom.html#bom4)if(b.Length>=4&& b[0]==0x00&& b[1]==0x00&& b[2]==0xFE&& b[3]==0xFF){ text =Encoding.GetEncoding("utf-32BE").GetString(b,4, b.Length-4);returnEncoding.GetEncoding("utf-32BE");}// UTF-32, big-endian elseif(b.Length>=4&& b[0]==0xFF&& b[1]==0xFE&& b[2]==0x00&& b[3]==0x00){ text =Encoding.UTF32.GetString(b,4, b.Length-4);returnEncoding.UTF32;}// UTF-32, little-endianelseif(b.Length>=2&& b[0]==0xFE&& b[1]==0xFF){ text =Encoding.BigEndianUnicode.GetString(b,2, b.Length-2);returnEncoding.BigEndianUnicode;}// UTF-16, big-endianelseif(b.Length>=2&& b[0]==0xFF&& b[1]==0xFE){ text =Encoding.Unicode.GetString(b,2, b.Length-2);returnEncoding.Unicode;}// UTF-16, little-endianelseif(b.Length>=3&& b[0]==0xEF&& b[1]==0xBB&& b[2]==0xBF){ text =Encoding.UTF8.GetString(b,3, b.Length-3);returnEncoding.UTF8;}// UTF-8elseif(b.Length>=3&& b[0]==0x2b&& b[1]==0x2f&& b[2]==0x76){ text =Encoding.UTF7.GetString(b,3,b.Length-3);returnEncoding.UTF7;}// UTF-7//////////// If the code reaches here, no BOM/signature was found, so now//////////// we need to 'taste' the file to see if can manually discover//////////// the encoding. A high taster value is desired for UTF-8if(taster ==0|| taster > b.Length) taster = b.Length;// Taster size can't be bigger than the filesize obviously.// Some text files are encoded in UTF8, but have no BOM/signature. Hence// the below manually checks for a UTF8 pattern. This code is based off// the top answer at: /programming/6555015/check-for-invalid-utf8// For our purposes, an unnecessarily strict (and terser/slower)// implementation is shown at: /programming/1031645/how-to-detect-utf-8-in-plain-c// For the below, false positives should be exceedingly rare (and would// be either slightly malformed UTF-8 (which would suit our purposes// anyway) or 8-bit extended ASCII/UTF-16/32 at a vanishingly long shot).int i =0;bool utf8 =false;while(i < taster -4){if(b[i]<=0x7F){ i +=1;continue;}// If all characters are below 0x80, then it is valid UTF8, but UTF8 is not 'required' (and therefore the text is more desirable to be treated as the default codepage of the computer). Hence, there's no "utf8 = true;" code unlike the next three checks.if(b[i]>=0xC2&& b[i]<=0xDF&& b[i +1]>=0x80&& b[i +1]<0xC0){ i +=2; utf8 =true;continue;}if(b[i]>=0xE0&& b[i]<=0xF0&& b[i +1]>=0x80&& b[i +1]<0xC0&& b[i +2]>=0x80&& b[i +2]<0xC0){ i +=3; utf8 =true;continue;}if(b[i]>=0xF0&& b[i]<=0xF4&& b[i +1]>=0x80&& b[i +1]<0xC0&& b[i +2]>=0x80&& b[i +2]<0xC0&& b[i +3]>=0x80&& b[i +3]<0xC0){ i +=4; utf8 =true;continue;}
utf8 =false;break;}if(utf8 ==true){
text =Encoding.UTF8.GetString(b);returnEncoding.UTF8;}// The next check is a heuristic attempt to detect UTF-16 without a BOM.// We simply look for zeroes in odd or even byte places, and if a certain// threshold is reached, the code is 'probably' UF-16. double threshold =0.1;// proportion of chars step 2 which must be zeroed to be diagnosed as utf-16. 0.1 = 10%int count =0;for(int n =0; n < taster; n +=2)if(b[n]==0) count++;if(((double)count)/ taster > threshold){ text =Encoding.BigEndianUnicode.GetString(b);returnEncoding.BigEndianUnicode;}
count =0;for(int n =1; n < taster; n +=2)if(b[n]==0) count++;if(((double)count)/ taster > threshold){ text =Encoding.Unicode.GetString(b);returnEncoding.Unicode;}// (little-endian)// Finally, a long shot - let's see if we can find "charset=xyz" or// "encoding=xyz" to identify the encoding:for(int n =0; n < taster-9; n++){if(((b[n +0]=='c'|| b[n +0]=='C')&&(b[n +1]=='h'|| b[n +1]=='H')&&(b[n +2]=='a'|| b[n +2]=='A')&&(b[n +3]=='r'|| b[n +3]=='R')&&(b[n +4]=='s'|| b[n +4]=='S')&&(b[n +5]=='e'|| b[n +5]=='E')&&(b[n +6]=='t'|| b[n +6]=='T')&&(b[n +7]=='='))||((b[n +0]=='e'|| b[n +0]=='E')&&(b[n +1]=='n'|| b[n +1]=='N')&&(b[n +2]=='c'|| b[n +2]=='C')&&(b[n +3]=='o'|| b[n +3]=='O')&&(b[n +4]=='d'|| b[n +4]=='D')&&(b[n +5]=='i'|| b[n +5]=='I')&&(b[n +6]=='n'|| b[n +6]=='N')&&(b[n +7]=='g'|| b[n +7]=='G')&&(b[n +8]=='='))){if(b[n +0]=='c'|| b[n +0]=='C') n +=8;else n +=9;if(b[n]=='"'|| b[n]=='\'') n++;int oldn = n;while(n < taster &&(b[n]=='_'|| b[n]=='-'||(b[n]>='0'&& b[n]<='9')||(b[n]>='a'&& b[n]<='z')||(b[n]>='A'&& b[n]<='Z'))){ n++;}byte[] nb =newbyte[n-oldn];Array.Copy(b, oldn, nb,0, n-oldn);try{string internalEnc =Encoding.ASCII.GetString(nb);
text =Encoding.GetEncoding(internalEnc).GetString(b);returnEncoding.GetEncoding(internalEnc);}catch{break;}// If C# doesn't recognize the name of the encoding, break.}}// If all else fails, the encoding is probably (though certainly not// definitely) the user's local codepage! One might present to the user a// list of alternative encodings as shown here: /programming/8509339/what-is-the-most-common-encoding-of-each-language// A full list can be found using Encoding.GetEncodings();
text =Encoding.Default.GetString(b);returnEncoding.Default;}
Cela fonctionne pour les fichiers cyrilliques (et probablement tous les autres) .eml (à partir de l'en-tête du jeu de caractères du courrier)
Nime Cloud
UTF-7 ne peut pas être décodé aussi naïvement, en fait; son préambule complet est plus long et comprend deux bits du premier caractère. Le système .Net semble n'avoir aucun support pour le système de préambule de UTF7.
Nyerguds
A travaillé pour moi quand aucune des autres méthodes que j'ai vérifiées n'a aidé! Merci Dan.
Tejasvi Hegde
Merci pour votre solution. Je l'utilise pour déterminer l'encodage de fichiers provenant de sources complètement différentes. Ce que j'ai trouvé cependant, c'est que si j'utilise une valeur de dégustation trop faible, le résultat peut être faux. (par exemple, le code renvoyait Encoding.Default pour un fichier UTF8, même si j'utilisais b.Length / 10 comme dégustateur.) Alors je me suis demandé quel était l'argument pour utiliser un dégustateur inférieur à b.Length? Il semble que je ne puisse conclure que Encoding.Default est acceptable si et seulement si j'ai scanné tout le fichier.
Sean
@Sean: C'est pour quand la vitesse compte plus que la précision, en particulier pour les fichiers qui peuvent atteindre des dizaines ou des centaines de mégaoctets. D'après mon expérience, même une faible valeur de dégustation peut donner des résultats corrects ~ 99,9% du temps. Votre expérience peut différer.
Dan W
33
Cela dépend d'où la chaîne «vient». Une chaîne .NET est Unicode (UTF-16). La seule façon dont cela pourrait être différent si, par exemple, vous lisiez les données d'une base de données dans un tableau d'octets.
Il est venu d'une application C ++ non Unicode. L'article CodeProject semble un peu trop complexe, mais il semble faire ce que je veux faire .. Merci ..
krebstar
18
Je sais que c'est un peu tard - mais pour être clair:
Une chaîne n'a pas vraiment de codage ... dans .NET, une chaîne est une collection d'objets char. Essentiellement, s'il s'agit d'une chaîne, elle a déjà été décodée.
Cependant, si vous lisez le contenu d'un fichier, qui est composé d'octets, et que vous souhaitez le convertir en chaîne, alors le codage du fichier doit être utilisé.
.NET comprend des classes d'encodage et de décodage pour: ASCII, UTF7, UTF8, UTF32 et plus.
La plupart de ces codages contiennent certaines marques d'ordre des octets qui peuvent être utilisées pour distinguer le type de codage utilisé.
La classe .NET System.IO.StreamReader est capable de déterminer le codage utilisé dans un flux, en lisant ces marques d'ordre des octets;
Voici un exemple:
/// <summary>/// return the detected encoding and the contents of the file./// </summary>/// <param name="fileName"></param>/// <param name="contents"></param>/// <returns></returns>publicstaticEncodingDetectEncoding(String fileName,outString contents){// open the file with the stream-reader:
using (StreamReader reader =newStreamReader(fileName,true)){// read the contents of the file into a string
contents = reader.ReadToEnd();// return the encoding.return reader.CurrentEncoding;}}
Cela ne fonctionnera pas pour détecter UTF 16 sans la nomenclature. Il ne reviendra pas non plus à la page de codes par défaut locale de l'utilisateur s'il ne détecte aucun encodage Unicode. Vous pouvez corriger ce dernier en l'ajoutant en Encoding.Defaulttant que paramètre StreamReader, mais le code ne détectera pas UTF8 sans la nomenclature.
Dan W
1
@DanW: Est-ce que UTF-16 sans BOM est déjà fait? Je n'utiliserais jamais ça; ce sera forcément un désastre d'ouvrir à peu près n'importe quoi.
Cette petite classe uniquement C # utilise BOMS si elle est présente, essaie de détecter automatiquement d'éventuels codages Unicode dans le cas contraire, et revient si aucun des codages Unicode n'est possible ou probable.
Cela ressemble à UTF8Checker référencé ci-dessus fait quelque chose de similaire, mais je pense que sa portée est légèrement plus large - au lieu de juste UTF8, il vérifie également d'autres encodages Unicode possibles (UTF-16 LE ou BE) qui pourraient manquer une nomenclature.
cela devrait être plus haut, cela fournit une solution très simple: laissez les autres faire le travail: D
buddybubble
Cette bibliothèque est GPL
A br
Vraiment? Je vois une licence MIT et elle utilise un composant à triple licence (UDE), dont MPL. J'ai essayé de déterminer si UDE était problématique pour un produit propriétaire, donc si vous avez plus d'informations, ce serait grandement apprécié.
Simon Woods
5
Ma solution consiste à utiliser des éléments intégrés avec quelques solutions de secours.
J'ai choisi la stratégie d'une réponse à une autre question similaire sur stackoverflow mais je ne la trouve pas maintenant.
Il vérifie d'abord la nomenclature en utilisant la logique intégrée de StreamReader, s'il y a une nomenclature, le codage sera autre chose que Encoding.Default, et nous devons faire confiance à ce résultat.
Sinon, il vérifie si la séquence d'octets est une séquence UTF-8 valide. si c'est le cas, il devinera UTF-8 comme encodage, et sinon, encore une fois, l'encodage ASCII par défaut sera le résultat.
Remarque: il s'agissait d'une expérience pour voir comment l'encodage UTF-8 fonctionnait en interne. La solution proposée par vilicvane , utiliser un UTF8Encodingobjet initialisé pour lever une exception en cas d'échec de décodage, est beaucoup plus simple et fait essentiellement la même chose.
J'ai écrit ce morceau de code pour différencier UTF-8 et Windows-1252. Cependant, il ne devrait pas être utilisé pour des fichiers texte gigantesques, car il charge le tout en mémoire et le scanne complètement. Je l'ai utilisé pour les fichiers de sous-titres .srt, juste pour pouvoir les sauvegarder dans l'encodage dans lequel ils étaient chargés.
Le codage donné à la fonction en tant que ref doit être le codage de secours 8 bits à utiliser au cas où le fichier serait détecté comme n'étant pas valide UTF-8; généralement, sur les systèmes Windows, ce sera Windows-1252. Cela n'a rien d'extraordinaire comme vérifier les plages ascii valides réelles, et ne détecte pas UTF-16 même sur la marque d'ordre d'octet.
Fondamentalement, la plage de bits du premier octet détermine combien après il fait partie de l'entité UTF-8. Ces octets après lui sont toujours dans la même plage de bits.
/// <summary>/// Reads a text file, and detects whether its encoding is valid UTF-8 or ascii./// If not, decodes the text using the given fallback encoding./// Bit-wise mechanism for detecting valid UTF-8 based on/// https://ianthehenry.com/2015/1/17/decoding-utf-8//// </summary>/// <param name="docBytes">The bytes read from the file.</param>/// <param name="encoding">The default encoding to use as fallback if the text is detected not to be pure ascii or UTF-8 compliant. This ref parameter is changed to the detected encoding.</param>/// <returns>The contents of the read file, as String.</returns>publicstaticStringReadFileAndGetEncoding(Byte[] docBytes,refEncoding encoding){if(encoding ==null)
encoding =Encoding.GetEncoding(1252);Int32 len = docBytes.Length;// byte order mark for utf-8. Easiest way of detecting encoding.if(len >3&& docBytes[0]==0xEF&& docBytes[1]==0xBB&& docBytes[2]==0xBF){
encoding =new UTF8Encoding(true);// Note that even when initialising an encoding to have// a BOM, it does not cut it off the front of the input.return encoding.GetString(docBytes,3, len -3);}Boolean isPureAscii =true;Boolean isUtf8Valid =true;for(Int32 i =0; i < len;++i){Int32 skip =TestUtf8(docBytes, i);if(skip ==0)continue;if(isPureAscii)
isPureAscii =false;if(skip <0){
isUtf8Valid =false;// if invalid utf8 is detected, there's no sense in going on.break;}
i += skip;}if(isPureAscii)
encoding =newASCIIEncoding();// pure 7-bit ascii.elseif(isUtf8Valid)
encoding =new UTF8Encoding(false);// else, retain given encoding. This should be an 8-bit encoding like Windows-1252.return encoding.GetString(docBytes);}/// <summary>/// Tests if the bytes following the given offset are UTF-8 valid, and/// returns the amount of bytes to skip ahead to do the next read if it is./// If the text is not UTF-8 valid it returns -1./// </summary>/// <param name="binFile">Byte array to test</param>/// <param name="offset">Offset in the byte array to test.</param>/// <returns>The amount of bytes to skip ahead for the next read, or -1 if the byte sequence wasn't valid UTF-8</returns>publicstaticInt32TestUtf8(Byte[] binFile,Int32 offset){// 7 bytes (so 6 added bytes) is the maximum the UTF-8 design could support,// but in reality it only goes up to 3, meaning the full amount is 4.constInt32 maxUtf8Length =4;Byte current = binFile[offset];if((current &0x80)==0)return0;// valid 7-bit ascii. Added length is 0 bytes.Int32 len = binFile.Length;for(Int32 addedlength =1; addedlength < maxUtf8Length;++addedlength){Int32 fullmask =0x80;Int32 testmask =0;// This code adds shifted bits to get the desired full mask.// If the full mask is [111]0 0000, then test mask will be [110]0 0000. Since this is// effectively always the previous step in the iteration I just store it each time.for(Int32 i =0; i <= addedlength;++i){
testmask = fullmask;
fullmask +=(0x80>>(i+1));}// figure out bit masks from levelif((current & fullmask)== testmask){if(offset + addedlength >= len)return-1;// Lookahead. Pattern of any following bytes is always 10xxxxxxfor(Int32 i =1; i <= addedlength;++i){if((binFile[offset + i]&0xC0)!=0x80)return-1;}return addedlength;}}// Value is greater than the maximum allowed for utf8. Deemed invalid.return-1;}
Il n'y a pas non plus de dernière elsedéclaration après if ((current & 0xE0) == 0xC0) { ... } else if ((current & 0xF0) == 0xE0) { ... } else if ((current & 0xF0) == 0xE0) { ... } else if ((current & 0xF8) == 0xF0) { ... }. Je suppose que le elsecas serait UTF8 invalide: isUtf8Valid = false;. Voudriez-vous?
hal le
@hal Ah, vrai ... depuis, j'ai mis à jour mon propre code avec un système plus général (et plus avancé) qui utilise une boucle allant jusqu'à 3 mais qui peut techniquement être modifiée pour boucler plus loin (les spécifications sont un peu floues à ce sujet ; il est possible d'étendre UTF-8 jusqu'à 6 octets ajoutés je pense, mais seulement 3 sont utilisés dans les implémentations actuelles), donc je n'ai pas mis à jour ce code.
Nyerguds
@hal l'a mis à jour avec ma nouvelle solution. Le principe reste le même, mais les masques de bits sont créés et vérifiés dans une boucle plutôt que tous écrits explicitement dans le code.
CharsetDetector contient des méthodes de détection de codage statique:
CharsetDetector.DetectFromFile()
CharsetDetector.DetectFromStream()
CharsetDetector.DetectFromBytes()
le résultat détecté est dans la classe DetectionResulta l'attribut Detectedqui est l'instance de la classe DetectionDetailavec les attributs ci-dessous:
EncodingName
Encoding
Confidence
ci-dessous est un exemple pour montrer l'utilisation:
// Program.cs
using System;
using System.Text;
using UtfUnknown;
namespace ConsoleExample{publicclassProgram{publicstaticvoidMain(string[] args){string filename =@"E:\new-file.txt";DetectDemo(filename);}/// <summary>/// Command line example: detect the encoding of the given file./// </summary>/// <param name="filename">a filename</param>publicstaticvoidDetectDemo(string filename){// Detect from FileDetectionResult result =CharsetDetector.DetectFromFile(filename);// Get the best DetectionDetectionDetail resultDetected = result.Detected;// detected result may be null.if(resultDetected !=null){// Get the alias of the found encodingstring encodingName = resultDetected.EncodingName;// Get the System.Text.Encoding of the found encoding (can be null if not available)Encoding encoding = resultDetected.Encoding;// Get the confidence of the found encoding (between 0 and 1)float confidence = resultDetected.Confidence;if(encoding !=null){Console.WriteLine($"Detection completed: {filename}");Console.WriteLine($"EncodingWebName: {encoding.WebName}{Environment.NewLine}Confidence: {confidence}");}else{Console.WriteLine($"Detection completed: {filename}");Console.WriteLine($"(Encoding is null){Environment.NewLine}EncodingName: {encodingName}{Environment.NewLine}Confidence: {confidence}");}}else{Console.WriteLine($"Detection failed: {filename}");}}}}
Réponses:
Découvrez Utf8Checker, c'est une classe simple qui fait exactement cela en code managé pur. http://utf8checker.codeplex.com
Remarque: comme déjà indiqué, "déterminer l'encodage" n'a de sens que pour les flux d'octets. Si vous avez une chaîne, elle est déjà encodée par quelqu'un en cours de route qui connaissait déjà ou a deviné l'encodage pour obtenir la chaîne en premier lieu.
la source
Le code ci-dessous présente les caractéristiques suivantes:
Comme d'autres l'ont dit, aucune solution ne peut être parfaite (et on ne peut certainement pas faire la différence entre les différents encodages ASCII étendus 8 bits utilisés dans le monde), mais nous pouvons être `` assez bons '' surtout si le développeur présente également à l'utilisateur une liste d'encodages alternatifs comme indiqué ici: Quel est l'encodage le plus courant de chaque langue?
Une liste complète des encodages peut être trouvée en utilisant
Encoding.GetEncodings();
la source
Cela dépend d'où la chaîne «vient». Une chaîne .NET est Unicode (UTF-16). La seule façon dont cela pourrait être différent si, par exemple, vous lisiez les données d'une base de données dans un tableau d'octets.
Cet article CodeProject peut être intéressant: Détecter l'encodage pour le texte entrant et sortant
Les chaînes de Jon Skeet en C # et .NET sont une excellente explication des chaînes .NET.
la source
Je sais que c'est un peu tard - mais pour être clair:
Une chaîne n'a pas vraiment de codage ... dans .NET, une chaîne est une collection d'objets char. Essentiellement, s'il s'agit d'une chaîne, elle a déjà été décodée.
Cependant, si vous lisez le contenu d'un fichier, qui est composé d'octets, et que vous souhaitez le convertir en chaîne, alors le codage du fichier doit être utilisé.
.NET comprend des classes d'encodage et de décodage pour: ASCII, UTF7, UTF8, UTF32 et plus.
La plupart de ces codages contiennent certaines marques d'ordre des octets qui peuvent être utilisées pour distinguer le type de codage utilisé.
La classe .NET System.IO.StreamReader est capable de déterminer le codage utilisé dans un flux, en lisant ces marques d'ordre des octets;
Voici un exemple:
la source
Encoding.Default
tant que paramètre StreamReader, mais le code ne détectera pas UTF8 sans la nomenclature.Une autre option, très tardive, désolé:
http://www.architectshack.com/TextFileEncodingDetector.ashx
Cette petite classe uniquement C # utilise BOMS si elle est présente, essaie de détecter automatiquement d'éventuels codages Unicode dans le cas contraire, et revient si aucun des codages Unicode n'est possible ou probable.
Cela ressemble à UTF8Checker référencé ci-dessus fait quelque chose de similaire, mais je pense que sa portée est légèrement plus large - au lieu de juste UTF8, il vérifie également d'autres encodages Unicode possibles (UTF-16 LE ou BE) qui pourraient manquer une nomenclature.
J'espère que cela aide quelqu'un!
la source
Le package Nuget SimpleHelpers.FileEncoding enveloppe un port C # du détecteur de jeu de caractères universel Mozilla dans une API très simple:
la source
Ma solution consiste à utiliser des éléments intégrés avec quelques solutions de secours.
J'ai choisi la stratégie d'une réponse à une autre question similaire sur stackoverflow mais je ne la trouve pas maintenant.
Il vérifie d'abord la nomenclature en utilisant la logique intégrée de StreamReader, s'il y a une nomenclature, le codage sera autre chose que
Encoding.Default
, et nous devons faire confiance à ce résultat.Sinon, il vérifie si la séquence d'octets est une séquence UTF-8 valide. si c'est le cas, il devinera UTF-8 comme encodage, et sinon, encore une fois, l'encodage ASCII par défaut sera le résultat.
la source
Remarque: il s'agissait d'une expérience pour voir comment l'encodage UTF-8 fonctionnait en interne. La solution proposée par vilicvane , utiliser un
UTF8Encoding
objet initialisé pour lever une exception en cas d'échec de décodage, est beaucoup plus simple et fait essentiellement la même chose.J'ai écrit ce morceau de code pour différencier UTF-8 et Windows-1252. Cependant, il ne devrait pas être utilisé pour des fichiers texte gigantesques, car il charge le tout en mémoire et le scanne complètement. Je l'ai utilisé pour les fichiers de sous-titres .srt, juste pour pouvoir les sauvegarder dans l'encodage dans lequel ils étaient chargés.
Le codage donné à la fonction en tant que ref doit être le codage de secours 8 bits à utiliser au cas où le fichier serait détecté comme n'étant pas valide UTF-8; généralement, sur les systèmes Windows, ce sera Windows-1252. Cela n'a rien d'extraordinaire comme vérifier les plages ascii valides réelles, et ne détecte pas UTF-16 même sur la marque d'ordre d'octet.
La théorie derrière la détection au niveau du bit peut être trouvée ici: https://ianthehenry.com/2015/1/17/decoding-utf-8/
Fondamentalement, la plage de bits du premier octet détermine combien après il fait partie de l'entité UTF-8. Ces octets après lui sont toujours dans la même plage de bits.
la source
else
déclaration aprèsif ((current & 0xE0) == 0xC0) { ... } else if ((current & 0xF0) == 0xE0) { ... } else if ((current & 0xF0) == 0xE0) { ... } else if ((current & 0xF8) == 0xF0) { ... }
. Je suppose que leelse
cas serait UTF8 invalide:isUtf8Valid = false;
. Voudriez-vous?J'ai trouvé une nouvelle bibliothèque sur GitHub: CharsetDetector / UTF-unknown
c'est aussi un port du Mozilla Universal Charset Detector basé sur d'autres référentiels.
CharsetDetector / UTF-unknown ont une classe nommée
CharsetDetector
.CharsetDetector
contient des méthodes de détection de codage statique:CharsetDetector.DetectFromFile()
CharsetDetector.DetectFromStream()
CharsetDetector.DetectFromBytes()
le résultat détecté est dans la classe
DetectionResult
a l'attributDetected
qui est l'instance de la classeDetectionDetail
avec les attributs ci-dessous:EncodingName
Encoding
Confidence
ci-dessous est un exemple pour montrer l'utilisation:
exemple de capture d'écran de résultat:
la source