Quels encodages? UTF-8 vs UTF-16, grand vs petit boutiste? Ou faites-vous référence aux anciennes pages de code MSDos, telles que shift-JIS ou cyrillique, etc.?
@Oded: Quote "La méthode getEncoding () retournera l'encodage qui a été mis en place (lisez le JavaDoc) pour le flux. Elle ne devinera pas l'encodage pour vous.".
Fábio Antunes
2
Pour une lecture de fond, joelonsoftware.com/articles/Unicode.html est une bonne lecture. S'il y a une chose que vous devez savoir sur le texte, c'est qu'il n'existe pas de texte brut.
Martijn
Réponses:
155
La StreamReader.CurrentEncodingpropriété renvoie rarement le codage de fichier texte correct pour moi. J'ai eu plus de succès pour déterminer l'endianité d'un fichier, en analysant sa marque d'ordre d'octet (BOM). Si le fichier n'a pas de nomenclature, cela ne peut pas déterminer le codage du fichier.
* MISE À JOUR le 4/08/2020 pour inclure la détection UTF-32LE et renvoyer un encodage correct pour UTF-32BE
/// <summary>/// Determines a text file's encoding by analyzing its byte order mark (BOM)./// Defaults to ASCII when detection of the text file's endianness fails./// </summary>/// <param name="filename">The text file to analyze.</param>/// <returns>The detected encoding.</returns>publicstaticEncodingGetEncoding(string filename){// Read the BOMvar bom =newbyte[4];
using (var file =newFileStream(filename,FileMode.Open,FileAccess.Read)){
file.Read(bom,0,4);}// Analyze the BOMif(bom[0]==0x2b&& bom[1]==0x2f&& bom[2]==0x76)returnEncoding.UTF7;if(bom[0]==0xef&& bom[1]==0xbb&& bom[2]==0xbf)returnEncoding.UTF8;if(bom[0]==0xff&& bom[1]==0xfe&& bom[2]==0&& bom[3]==0)returnEncoding.UTF32;//UTF-32LEif(bom[0]==0xff&& bom[1]==0xfe)returnEncoding.Unicode;//UTF-16LEif(bom[0]==0xfe&& bom[1]==0xff)returnEncoding.BigEndianUnicode;//UTF-16BEif(bom[0]==0&& bom[1]==0&& bom[2]==0xfe&& bom[3]==0xff)returnnew UTF32Encoding(true,true);//UTF-32BE// We actually have no idea what the encoding is if we reach this point, so// you may wish to return null instead of defaulting to ASCIIreturnEncoding.ASCII;}
+1. Cela a fonctionné pour moi aussi (alors que detectEncodingFromByteOrderMarks n'a pas fonctionné). J'ai utilisé "new FileStream (filename, FileMode.Open, FileAccess.Read)" pour éviter une IOException car le fichier est en lecture seule.
Polyfun
56
Les fichiers UTF-8 peuvent être sans nomenclature, dans ce cas, ils renverront ASCII de manière incorrecte.
user626528
3
Cette réponse est fausse. En regardant la source de référence pour StreamReader, cette implémentation est ce que plus de gens voudront. Ils font de nouveaux encodages plutôt que d'utiliser les Encoding.Unicodeobjets existants , donc les vérifications d'égalité échoueront (ce qui peut rarement arriver de toute façon car, par exemple, Encoding.UTF8peut renvoyer des objets différents), mais il (1) n'utilise pas le format UTF-7 vraiment étrange, (2) prend la valeur par défaut UTF-8 si aucune nomenclature n'est trouvée, et (3) peut être remplacé pour utiliser un codage par défaut différent.
hangar
2
J'ai eu un meilleur succès avec le nouveau StreamReader (nom de fichier, true) .CurrentEncoding
Benoit
4
Il y a une erreur fondamentale dans le code; lorsque vous détectez la signature UTF32 big-endian ( ), vous retournez le système fourni , qui est un encodage little-endian (comme indiqué ici ). Et aussi, comme indiqué par @Nyerguds, vous ne recherchez toujours pas UTF32LE, qui a une signature (selon en.wikipedia.org/wiki/Byte_order_mark ). Comme cet utilisateur l'a noté, parce qu'il est subsumant, ce contrôle doit venir avant les contrôles à 2 octets. 00 00 FE FFEncoding.UTF32FF FE 00 00
Glenn Slayden
44
Le code suivant fonctionne très bien pour moi, en utilisant la StreamReaderclasse:
using (var reader =newStreamReader(fileName, defaultEncodingIfNoBom,true)){
reader.Peek();// you need this!var encoding = reader.CurrentEncoding;}
L'astuce consiste à utiliser l' Peekappel, sinon .NET n'a rien fait (et il n'a pas lu le préambule, la nomenclature). Bien sûr, si vous utilisez un autre ReadXXXappel avant de vérifier l'encodage, cela fonctionne aussi.
Si le fichier n'a pas de nomenclature, le defaultEncodingIfNoBomcodage sera utilisé. Il existe également un StreamReader sans cette méthode de surcharge (dans ce cas, l'encodage par défaut (ANSI) sera utilisé comme defaultEncodingIfNoBom), mais je vous recommande de définir ce que vous considérez comme l'encodage par défaut dans votre contexte.
J'ai testé cela avec succès avec des fichiers avec BOM pour UTF8, UTF16 / Unicode (LE & BE) et UTF32 (LE & BE). Cela ne fonctionne pas pour UTF7.
Je récupère ce qui est défini comme encodage par défaut. Pourrais-je manquer quelque chose?
Ram
1
@DRAM - cela peut arriver si le fichier n'a pas de nomenclature
Simon Mourier
Merci @Simon Mourier. Je ne m'attendais pas à ce que mon pdf / n'importe quel fichier ne soit pas né. Ce lien stackoverflow.com/questions/4520184/… peut être utile pour quelqu'un qui essaie de détecter sans bom.
Ram
1
Dans PowerShell, j'ai dû exécuter $ reader.close (), sinon il était verrouillé contre l'écriture. foreach($filename in $args) { $reader = [System.IO.StreamReader]::new($filename, [System.Text.Encoding]::default,$true); $peek = $reader.Peek(); $reader.currentencoding | select bodyname,encodingname; $reader.close() }
js2010
1
@SimonMourier Cela ne fonctionne pas si l'encodage du fichier estUTF-8 without BOM
Ozkan
11
J'essaierais les étapes suivantes:
1) Vérifiez s'il y a une marque d'ordre d'octet
2) Vérifiez si le fichier est valide UTF8
3) Utilisez la page de codes locale «ANSI» (ANSI comme le définit Microsoft)
L'étape 2 fonctionne car la plupart des séquences non ASCII des pages de code autres que UTF8 ne sont pas UTF8 valides.
Cela semble être la réponse la plus correcte, car l'autre réponse ne fonctionne pas pour moi. On peut le faire avec File.OpenRead et .Lire les premiers octets du fichier.
user420667
1
L'étape 2 est tout un tas de travaux de programmation pour vérifier les modèles de bits, cependant.
Nyerguds
1
Je ne suis pas sûr que le décodage lève réellement des exceptions, ou s'il remplace simplement les séquences non reconnues par '?'. Je suis allé avec l'écriture d'une petite classe de vérification de modèle, de toute façon.
Nyerguds
3
Lorsque vous créez une instance de, Utf8Encodingvous pouvez transmettre un paramètre supplémentaire qui détermine si une exception doit être levée ou si vous préférez une corruption silencieuse des données.
CodesInChaos
1
J'aime cette réponse. La plupart des encodages (comme 99% de vos cas d'utilisation probablement) seront soit UTF-8, soit ANSI (page de codes Windows 1252). Vous pouvez vérifier si la chaîne contient le caractère de remplacement (0xFFFD) pour déterminer si l'encodage a échoué.
Ok si vous êtes inquiet pour la licence, vous pouvez utiliser celle-ci. Sous licence MIT et vous pouvez l'utiliser à la fois pour les logiciels open source et fermés. nuget.org/packages/SimpleHelpers.FileEncoding
Alexei Agüero Alba
La licence est MPL avec une option GPL. The library is subject to the Mozilla Public License Version 1.1 (the "License"). Alternatively, it may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL").
jbtule
Il semble que ce fork soit actuellement le plus actif et qu'il possède un package nuget UDE.Netstandard. github.com/yinyue200/ude
jbtule
bibliothèque très utile, avec beaucoup d'encodages différents et inhabituels! réservoirs!
mshakurov
6
Fournir les détails d'implémentation des étapes proposées par @CodesInChaos:
1) Vérifiez s'il y a une marque d'ordre d'octet
2) Vérifiez si le fichier est valide UTF8
3) Utilisez la page de codes locale «ANSI» (ANSI comme le définit Microsoft)
L'étape 2 fonctionne car la plupart des séquences non ASCII dans les pages de code autres que UTF8 ne sont pas UTF8 valides. https://stackoverflow.com/a/4522251/867248 explique la tactique plus en détail.
using System; using System.IO; using System.Text;// Using encoding from BOM or UTF8 if no BOM found,// check if the file is valid, by reading all lines// If decoding fails, use the local "ANSI" codepagepublicstringDetectFileEncoding(Stream fileStream){varUtf8EncodingVerifier=Encoding.GetEncoding("utf-8",newEncoderExceptionFallback(),newDecoderExceptionFallback());
using (var reader =newStreamReader(fileStream,Utf8EncodingVerifier,
detectEncodingFromByteOrderMarks:true, leaveOpen:true, bufferSize:1024)){string detectedEncoding;try{while(!reader.EndOfStream){var line = reader.ReadLine();}
detectedEncoding = reader.CurrentEncoding.BodyName;}catch(Exception e){// Failed to decode the file using the BOM/UT8. // Assume it's local ANSI
detectedEncoding ="ISO-8859-1";}// Rewind the stream
fileStream.Seek(0,SeekOrigin.Begin);return detectedEncoding;}}[Test]publicvoidTest1(){Stream fs =File.OpenRead(@".\TestData\TextFile_ansi.csv");var detectedEncoding =DetectFileEncoding(fs);
using (var reader =newStreamReader(fs,Encoding.GetEncoding(detectedEncoding))){// Consume your filevar line = reader.ReadLine();...
Je vous remercie! Cela a résolu pour moi. Mais je préférerais utiliser juste reader.Peek() au lieu de while (!reader.EndOfStream) { var line = reader.ReadLine(); }
Harison Silva
reader.Peek()ne lit pas tout le flux. J'ai trouvé qu'avec des flux plus importants, Peek()c'était insuffisant. J'ai utilisé à la reader.ReadToEndAsync()place.
Gary Pendlebury
Et qu'est-ce que Utf8EncodingVerifier?
Peter Moore
1
@PeterMoore C'est un encodage pour utf8, var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());Il est utilisé dans le trybloc lors de la lecture d'une ligne. Si l'encodeur ne parvient pas à analyser le texte fourni (le texte n'est pas encodé avec utf8), Utf8EncodingVerifier lancera. L'exception est interceptée et on sait alors que le texte n'est pas utf8, et par défaut ISO-8859-1
Berthier Lemieux
2
Les codes suivants sont mes codes Powershell pour déterminer si certains fichiers cpp ou h ou ml sont encodés avec ISO-8859-1 (Latin-1) ou UTF-8 sans BOM, si ni l'un ni l'autre, supposons que ce soit GB18030. Je suis chinois travaillant en France et MSVC enregistre en Latin-1 sur un ordinateur français et enregistre en Go sur un ordinateur chinois, ce qui m'aide à éviter les problèmes d'encodage lors des échanges de fichiers source entre mon système et mes collègues.
Le chemin est simple, si tous les caractères sont entre x00-x7E, ASCII, UTF-8 et Latin-1 sont tous les mêmes, mais si je lis un fichier non ASCII par UTF-8, nous trouverons le caractère spécial apparaître , alors essayez de lire avec Latin-1. En Latin-1, entre \ x7F et \ xAF est vide, tandis que GB utilise plein entre x00-xFF donc si j'en ai entre les deux, ce n'est pas Latin-1
Le code est écrit en PowerShell, mais utilise .net, il est donc facile d'être traduit en C # ou F #
.NET n'est pas très utile, mais vous pouvez essayer l'algorithme suivant:
essayez de trouver le codage par BOM (marque d'ordre d'octet) ... très probablement introuvable
essayez d'analyser différents encodages
Voici l'appel:
var encoding =FileHelper.GetEncoding(filePath);if(encoding ==null)thrownewException("The file encoding is not supported. Please choose one of the following encodings: UTF8/UTF7/iso-8859-1");
Voici le code:
publicclassFileHelper{/// <summary>/// Determines a text file's encoding by analyzing its byte order mark (BOM) and if not found try parsing into diferent encodings /// Defaults to UTF8 when detection of the text file's endianness fails./// </summary>/// <param name="filename">The text file to analyze.</param>/// <returns>The detected encoding or null.</returns>publicstaticEncodingGetEncoding(string filename){var encodingByBOM =GetEncodingByBOM(filename);if(encodingByBOM !=null)return encodingByBOM;// BOM not found :(, so try to parse characters into several encodingsvar encodingByParsingUTF8 =GetEncodingByParsing(filename,Encoding.UTF8);if(encodingByParsingUTF8 !=null)return encodingByParsingUTF8;var encodingByParsingLatin1 =GetEncodingByParsing(filename,Encoding.GetEncoding("iso-8859-1"));if(encodingByParsingLatin1 !=null)return encodingByParsingLatin1;var encodingByParsingUTF7 =GetEncodingByParsing(filename,Encoding.UTF7);if(encodingByParsingUTF7 !=null)return encodingByParsingUTF7;returnnull;// no encoding found}/// <summary>/// Determines a text file's encoding by analyzing its byte order mark (BOM) /// </summary>/// <param name="filename">The text file to analyze.</param>/// <returns>The detected encoding.</returns>privatestaticEncodingGetEncodingByBOM(string filename){// Read the BOMvar byteOrderMark =newbyte[4];
using (var file =newFileStream(filename,FileMode.Open,FileAccess.Read)){
file.Read(byteOrderMark,0,4);}// Analyze the BOMif(byteOrderMark[0]==0x2b&& byteOrderMark[1]==0x2f&& byteOrderMark[2]==0x76)returnEncoding.UTF7;if(byteOrderMark[0]==0xef&& byteOrderMark[1]==0xbb&& byteOrderMark[2]==0xbf)returnEncoding.UTF8;if(byteOrderMark[0]==0xff&& byteOrderMark[1]==0xfe)returnEncoding.Unicode;//UTF-16LEif(byteOrderMark[0]==0xfe&& byteOrderMark[1]==0xff)returnEncoding.BigEndianUnicode;//UTF-16BEif(byteOrderMark[0]==0&& byteOrderMark[1]==0&& byteOrderMark[2]==0xfe&& byteOrderMark[3]==0xff)returnEncoding.UTF32;returnnull;// no BOM found}privatestaticEncodingGetEncodingByParsing(string filename,Encoding encoding){var encodingVerifier =Encoding.GetEncoding(encoding.BodyName,newEncoderExceptionFallback(),newDecoderExceptionFallback());try{
using (var textReader =newStreamReader(filename, encodingVerifier, detectEncodingFromByteOrderMarks:true)){while(!textReader.EndOfStream){
textReader.ReadLine();// in order to increment the stream position}// all text parsed okreturn textReader.CurrentEncoding;}}catch(Exception ex){}returnnull;// }}
string path =@"path\to\your\file.ext";
using (StreamReader sr =newStreamReader(path,true)){while(sr.Peek()>=0){Console.Write((char)sr.Read());}//Test for the encoding after reading, or at least//after the first read.Console.WriteLine("The encoding used was {0}.", sr.CurrentEncoding);Console.ReadLine();Console.WriteLine();}
Réponses:
La
StreamReader.CurrentEncoding
propriété renvoie rarement le codage de fichier texte correct pour moi. J'ai eu plus de succès pour déterminer l'endianité d'un fichier, en analysant sa marque d'ordre d'octet (BOM). Si le fichier n'a pas de nomenclature, cela ne peut pas déterminer le codage du fichier.* MISE À JOUR le 4/08/2020 pour inclure la détection UTF-32LE et renvoyer un encodage correct pour UTF-32BE
la source
StreamReader
, cette implémentation est ce que plus de gens voudront. Ils font de nouveaux encodages plutôt que d'utiliser lesEncoding.Unicode
objets existants , donc les vérifications d'égalité échoueront (ce qui peut rarement arriver de toute façon car, par exemple,Encoding.UTF8
peut renvoyer des objets différents), mais il (1) n'utilise pas le format UTF-7 vraiment étrange, (2) prend la valeur par défaut UTF-8 si aucune nomenclature n'est trouvée, et (3) peut être remplacé pour utiliser un codage par défaut différent.00 00 FE FF
Encoding.UTF32
FF FE 00 00
Le code suivant fonctionne très bien pour moi, en utilisant la
StreamReader
classe:L'astuce consiste à utiliser l'
Peek
appel, sinon .NET n'a rien fait (et il n'a pas lu le préambule, la nomenclature). Bien sûr, si vous utilisez un autreReadXXX
appel avant de vérifier l'encodage, cela fonctionne aussi.Si le fichier n'a pas de nomenclature, le
defaultEncodingIfNoBom
codage sera utilisé. Il existe également un StreamReader sans cette méthode de surcharge (dans ce cas, l'encodage par défaut (ANSI) sera utilisé comme defaultEncodingIfNoBom), mais je vous recommande de définir ce que vous considérez comme l'encodage par défaut dans votre contexte.J'ai testé cela avec succès avec des fichiers avec BOM pour UTF8, UTF16 / Unicode (LE & BE) et UTF32 (LE & BE). Cela ne fonctionne pas pour UTF7.
la source
foreach($filename in $args) { $reader = [System.IO.StreamReader]::new($filename, [System.Text.Encoding]::default,$true); $peek = $reader.Peek(); $reader.currentencoding | select bodyname,encodingname; $reader.close() }
UTF-8 without BOM
J'essaierais les étapes suivantes:
1) Vérifiez s'il y a une marque d'ordre d'octet
2) Vérifiez si le fichier est valide UTF8
3) Utilisez la page de codes locale «ANSI» (ANSI comme le définit Microsoft)
L'étape 2 fonctionne car la plupart des séquences non ASCII des pages de code autres que UTF8 ne sont pas UTF8 valides.
la source
Utf8Encoding
vous pouvez transmettre un paramètre supplémentaire qui détermine si une exception doit être levée ou si vous préférez une corruption silencieuse des données.Vérifie ça.
UDE
Ceci est un port de Mozilla Universal Charset Detector et vous pouvez l'utiliser comme ceci ...
la source
The library is subject to the Mozilla Public License Version 1.1 (the "License"). Alternatively, it may be used under the terms of either the GNU General Public License Version 2 or later (the "GPL"), or the GNU Lesser General Public License Version 2.1 or later (the "LGPL").
Fournir les détails d'implémentation des étapes proposées par @CodesInChaos:
1) Vérifiez s'il y a une marque d'ordre d'octet
2) Vérifiez si le fichier est valide UTF8
3) Utilisez la page de codes locale «ANSI» (ANSI comme le définit Microsoft)
L'étape 2 fonctionne car la plupart des séquences non ASCII dans les pages de code autres que UTF8 ne sont pas UTF8 valides. https://stackoverflow.com/a/4522251/867248 explique la tactique plus en détail.
la source
reader.Peek()
au lieu dewhile (!reader.EndOfStream) { var line = reader.ReadLine(); }
reader.Peek()
ne lit pas tout le flux. J'ai trouvé qu'avec des flux plus importants,Peek()
c'était insuffisant. J'ai utilisé à lareader.ReadToEndAsync()
place.var Utf8EncodingVerifier = Encoding.GetEncoding("utf-8", new EncoderExceptionFallback(), new DecoderExceptionFallback());
Il est utilisé dans letry
bloc lors de la lecture d'une ligne. Si l'encodeur ne parvient pas à analyser le texte fourni (le texte n'est pas encodé avec utf8), Utf8EncodingVerifier lancera. L'exception est interceptée et on sait alors que le texte n'est pas utf8, et par défaut ISO-8859-1Les codes suivants sont mes codes Powershell pour déterminer si certains fichiers cpp ou h ou ml sont encodés avec ISO-8859-1 (Latin-1) ou UTF-8 sans BOM, si ni l'un ni l'autre, supposons que ce soit GB18030. Je suis chinois travaillant en France et MSVC enregistre en Latin-1 sur un ordinateur français et enregistre en Go sur un ordinateur chinois, ce qui m'aide à éviter les problèmes d'encodage lors des échanges de fichiers source entre mon système et mes collègues.
Le chemin est simple, si tous les caractères sont entre x00-x7E, ASCII, UTF-8 et Latin-1 sont tous les mêmes, mais si je lis un fichier non ASCII par UTF-8, nous trouverons le caractère spécial apparaître , alors essayez de lire avec Latin-1. En Latin-1, entre \ x7F et \ xAF est vide, tandis que GB utilise plein entre x00-xFF donc si j'en ai entre les deux, ce n'est pas Latin-1
Le code est écrit en PowerShell, mais utilise .net, il est donc facile d'être traduit en C # ou F #
la source
.NET n'est pas très utile, mais vous pouvez essayer l'algorithme suivant:
Voici l'appel:
Voici le code:
la source
Regardez ici pour c #
https://msdn.microsoft.com/en-us/library/system.io.streamreader.currentencoding%28v=vs.110%29.aspx
la source
Cela peut être utile
la source