Первым шагом является загрузка файла в виде байтового массива вместо строки. Строки всегда хранятся в памяти с кодировкой 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#, вы также можете найти ответы на него полезными.
Вы не можете надежно сделать это. –
http://stackoverflow.com/questions/3825390/c-sharp-effective-way-to-find-any-files-encoding – adripanico
@adripanico, пожалуйста, просмотрите комментарии под этим ответом, я также протестировал его, но он возвращает кодировку VS, а не кодировку файла. он возвращает «UTF8», когда файл находится в кодировке ANSI. – ElektroStudios