2013-09-20 2 views
15

мне нужно, чтобы определить, если содержимое текстового файла равно одному из этих кодировок:Определить кодировку TextFile?

System.Text.Encoding.ASCII 
System.Text.Encoding.BigEndianUnicode ' UTF-L 16 
System.Text.Encoding.Default ' ANSI 
System.Text.Encoding.Unicode ' UTF16 
System.Text.Encoding.UTF32 
System.Text.Encoding.UTF7 
System.Text.Encoding.UTF8 

Я не знаю, как читать байты метки файлов, я видел фрагменты делает это, но только может определить, является ли файл ASCII или Unicode, поэтому мне нужно что-то более универсальное.

+2

Вы не можете надежно сделать это. –

+0

http://stackoverflow.com/questions/3825390/c-sharp-effective-way-to-find-any-files-encoding – adripanico

+0

@adripanico, пожалуйста, просмотрите комментарии под этим ответом, я также протестировал его, но он возвращает кодировку VS, а не кодировку файла. он возвращает «UTF8», когда файл находится в кодировке ANSI. – ElektroStudios

ответ

48

Первым шагом является загрузка файла в виде байтового массива вместо строки. Строки всегда хранятся в памяти с кодировкой UTF-16, поэтому после ее загрузки в строку исходная кодировка теряется. Вот простой пример один из способов загрузки файла в массив байтов:

Dim data() As Byte = File.ReadAllBytes("test.txt") 

Автоматическое определение правильного кодирования для заданного массива байтов, как известно, трудно. Иногда, чтобы быть полезным, автор данных вставляет данные, которые называются BOM (Byte Order Mark) в начале данных. Если присутствует спецификация, это делает отсутствие кодирования безболезненным, поскольку каждая кодировка использует другую спецификацию.

Самый простой способ автоматического определения кодировки из спецификации - позволить StreamReader сделать это за вас. В конструкторе StreamReader вы можете передать True для аргумента detectEncodingFromByteOrderMarks. Затем вы можете получить кодировку потока, обратившись к свойству CurrentEncoding. Однако свойство CurrentEncoding не будет работать до тех пор, пока StreamReader не прочитает спецификацию. Таким образом, вы сначала должны прочитать мимо BOM, прежде чем вы можете получить кодировку, например:

Public Function GetFileEncoding(filePath As String) As Encoding 
    Using sr As New StreamReader(filePath, True) 
     sr.Read() 
     Return sr.CurrentEncoding 
    End Using 
End Function 

Однако проблема такого подхода заключается в том, что MSDN, кажется, подразумевает, что StreamReader может обнаружить только определенные виды encodingings:

Параметр detectEncodingFromByteOrderMarks определяет кодировку, просматривая первые три байта потока. Он автоматически распознает UTF-8, малоконечный Unicode и текстовый текст в формате Unicode, если файл начинается с соответствующих меток байтового байта. Дополнительную информацию см. В методе Encoding.GetPreamble.

Кроме того, если StreamReader не в состоянии определить кодировку из спецификации, или если BOM не существует, он будет только по умолчанию UTF-8 кодировке, не давая вам никаких признаков того, что она не удалась. Если вам требуется более подробный контроль, вы можете легко прочитать спецификацию и интерпретировать ее самостоятельно. Все, что вам нужно сделать, это сравнить первые несколько байтов в массиве байтов с некоторыми известными ожидаемыми спецификациями, чтобы увидеть, соответствуют ли они.Вот список некоторых распространенного Борна:

  • UTF-8: EF BB BF
  • UTF-16 большой заказ младших байт: FE FF
  • UTF-16 маленький порядок обратного порядка байт байт: FF FE
  • UTF-32 большой заказ младших байт: 00 00 FE FF
  • UTF-32 маленький порядок байт обратного порядка байт: FF FE 00 00

Так, например, чтобы увидеть, если UTF-16 (прямой порядок байт) BOM существует в начале массива байт, вы можете просто сделать что-то вроде этого:

If (data(0) = &HFF) And (data(1) = &HFE) Then 
    ' Data starts with UTF-16 (little endian) BOM 
End If 

Удобно, Encoding класса в .NET содержит метод, называемый GetPreamble, который возвращает спецификацию, используемую кодировкой, поэтому вам даже не нужно помнить, что это все. Таким образом, чтобы проверить, является ли байт-массив начинается с BOM для Unicode (UTF-16, прямой порядок байтов), вы можете просто сделать это:

Function IsUtf16LittleEndian(data() as Byte) As Boolean 
    Dim bom() As Byte = Encoding.Unicode.GetPreamble() 
    If (data(0) = bom(0)) And (data(1) = bom(1) Then 
     Return True 
    Else 
     Return False 
    End If 
End Function 

Конечно, данная функция предполагает, что данные в не более двух байтов, а спецификация - это всего два байта. Итак, хотя это иллюстрирует, как сделать это как можно более ясно, это не самый безопасный способ сделать это. Для того, чтобы сделать его терпимым различную длину массива, особенно, так как BOM длиной сам могут варьироваться от одной кодировки в другую, было бы безопаснее, чтобы сделать что-то вроде этого:

Function IsUtf16LittleEndian(data() as Byte) As Boolean 
    Dim bom() As Byte = Encoding.Unicode.GetPreamble() 
    Return data.Zip(bom, Function(x, y) x = y).All(Function(x) x) 
End Function 

Таким образом, проблема становится, как вы получаете список всех кодировок? Ну, так получилось, что класс .NET Encoding также предоставляет общий (статический) метод под названием GetEncodings, который возвращает список всех поддерживаемых объектов кодирования. Таким образом, вы можете создать метод, который проходит через все объекты кодирования, получает спецификацию каждого из них и сравнивает его с массивом байтов, пока не найдете тот, который соответствует. Например:

Public Function DetectEncodingFromBom(data() As Byte) As Encoding 
    Return Encoding.GetEncodings(). 
     Select(Function(info) info.GetEncoding()). 
     FirstOrDefault(Function(enc) DataStartsWithBom(data, enc)) 
End Function 

Private Function DataStartsWithBom(data() As Byte, enc As Encoding) As Boolean 
    Dim bom() As Byte = enc.GetPreamble() 
    If bom.Length <> 0 Then 
     Return data. 
      Zip(bom, Function(x, y) x = y). 
      All(Function(x) x) 
    Else 
     Return False 
    End If 
End Function 

После того, как вы сделаете такую ​​функцию, что, то вы могли бы определить кодировку файла, как это:

Dim data() As Byte = File.ReadAllBytes("test.txt") 
Dim detectedEncoding As Encoding = DetectEncodingFromBom(data) 
If detectedEncoding Is Nothing Then 
    Console.WriteLine("Unable to detect encoding") 
Else 
    Console.WriteLine(detectedEncoding.EncodingName) 
End If 

Однако проблема остается, как вы автоматически обнаружить правильный кодирования, когда нет спецификации? Технически рекомендуется не размещать спецификацию в начале ваших данных при использовании UTF-8, и нет спецификации, определенной для любой из кодовых страниц ANSI. Поэтому, конечно, не исключено, что текстовый файл может не иметь спецификации. Если все файлы, с которыми вы имеете дело, находятся на английском языке, вероятно, можно с уверенностью предположить, что если никакой спецификации нет, то UTF-8 будет достаточным. Однако, если какой-либо из файлов использует что-то другое, без спецификации, это не сработает.

Как вы правильно заметили, есть приложения, которые по-прежнему автоматически обнаруживают кодирование, даже если нет спецификации, но они делают это с помощью эвристики (то есть образованных предположений), а иногда и неточны. В основном они загружают данные с использованием каждой кодировки, а затем видят, что данные «выглядят» понятными. This page предлагает некоторые интересные сведения о проблемах внутри алгоритма автоматического обнаружения Notepad. This page показывает, как вы можете использовать алгоритм автоматического обнаружения на основе COM, который использует Internet Explorer (в C#).Вот список некоторых библиотек # C, что написали люди, которые пытаются автоматически определить кодировку массива байт, который может оказаться полезным:

Несмотря на то, что this question был для C#, вы также можете найти ответы на него полезными.

+1

Если бы я мог установить в свою очередь свои любимые ответы, это было бы одним из них, большое спасибо! – ElektroStudios

+1

Спасибо! Я добавил информацию о том, как это сделать с помощью StreamReader, который является важным моментом для включения. Я думаю, что причина, по которой он терпел неудачу для людей, в том, что другой ответ C#, который был помечен как дубликат, заключается в том, что они не делали 'Read' в потоке, прежде чем получить кодировку. Пока он не прочитает спецификацию, он просто вернет стандартную кодировку UTF-8. –

+0

Метод StreamReader возвращает UTF8 для файлов ANSI, я по-прежнему предпочитаю первый метод, который вы написали, поскольку он обнаруживает хорошие файлы UTF8, а также если обнаружена какая-либо кодировка, я могу вернуть шанс «наиболее вероятно кодирования» в качестве кодировки ANSI, и что работал очень хорошо для меня, чтобы обнаружить файлы ANSI и файл UTF, но я думаю, что то же самое не может быть сделано с помощью метода sr в нескольких строках, еще раз спасибо! – ElektroStudios

Смежные вопросы