Як я можу виявити кодування / кодову сторінку текстового файлу


295

У нашому додатку ми отримуємо текстові файли ( .txt, .csvі т.д.) з різних джерел. Під час читання ці файли іноді містять сміття, оскільки файли там, де створені в іншій / невідомій кодовій сторінці.

Чи є спосіб (автоматично) виявити кодову сторінку текстового файлу?

detectEncodingFromByteOrderMarks, На StreamReaderконструкторі, працює UTF8 і інші Юнікод зазначених файлів, але я шукав спосіб виявлення кодових сторінок, як ibm850, windows1252.


Дякую за відповіді, це я і зробив.

Файли, які ми отримуємо, - від кінцевих користувачів, вони не мають поняття про кодові сторінки. Одержувачі також є кінцевими споживачами, на сьогоднішній день це те, що вони знають про кодові сторінки: кодекси існують і дратують.

Рішення:

  • Відкрийте отриманий файл у Блокноті, подивіться на пошарпаний фрагмент тексту. Якщо когось називають Франсуа або щось подібне, з вашим людським інтелектом ви можете це здогадатися.
  • Я створив невеликий додаток, з яким користувач може відкрити файл, і ввести текст, який користувач знає, що він з’явиться у файлі, коли буде використана правильна сторінка коду.
  • Перегляньте всі кодові сторінки та виведіть ті, що дають рішення із наданим користувачем текстом.
  • Якщо з'являється більше, ніж одна сторінка коду, попросіть користувача вказати більше тексту.

Відповіді:


260

Ви не можете виявити кодову сторінку, вам це потрібно повідомити. Ви можете проаналізувати байти і здогадатися, але це може дати певні химерні (іноді кумедні) результати. Зараз я не можу його знайти, але я впевнений, що Блокнот може бути надуманий показувати англійський текст китайською мовою.

У будь-якому випадку, це те, що вам потрібно прочитати: Абсолютний мінімум кожного розробника програмного забезпечення абсолютно, позитивно повинен знати про набори Unicode та символів (без виправдань!) .

Конкретно Джоел говорить:

Єдиний найважливіший факт про кодування

Якщо ви повністю забудете все, що я щойно пояснив, запам’ятайте один надзвичайно важливий факт. Немає сенсу мати рядок, не знаючи, яке кодування воно використовує. Ви більше не можете засунути голову в пісок і зробити вигляд, що "звичайний" текст - це ASCII. Немає такої речі, як звичайний текст.

Якщо у вас є рядок, пам'ять, файл або повідомлення електронної пошти, ви повинні знати, що таке кодування, або ви не можете його інтерпретувати або відображати користувачам правильно.


43
Я відповів на цю відповідь з двох причин. По-перше, сказати, що "вам потрібно сказати" не корисно. Хто сказав би мені, і через яке середовище вони б це зробили? Якщо я той, хто врятував файл, кого б я запитав? Я сам? По-друге, стаття не особливо корисна як ресурс для відповіді на запитання. Стаття - це більше історія кодування, написана в стилі Девіда Седаріса. Я ціную розповідь, але це не просто / безпосередньо відповідає на питання.
генеорама

9
@geneorama, я думаю, що стаття Джоеля вирішує ваші запитання краще, ніж я коли-небудь міг, але тут ідеться ... Засіб, безумовно, залежить від середовища, в якому текст надходить. Краще, щоб у файлі (або будь-якому іншому) містилася ця інформація (я думаю, що HTML і XML). В іншому випадку особі, що надсилає текст, слід дозволити надати цю інформацію. Якщо ви створили файл, як ви не можете знати, яке кодування він використовує?
СП.

4
@geneorama, продовження ... Нарешті, я вважаю, що головна причина, в якій стаття не відповідає на питання, просто в тому, що на це питання немає простої відповіді. Якби питання було "Як я здогадуюсь ...", я би відповів інакше.
СП.

1
@JV Пізніше я дізнався, що xml / html може вказати кодування символів, дякую за те, що згадав про цей корисний прискік.
генеорама

1
@JV "Створити файл" може бути поганим вибором слів. Я припускаю, що користувач може вказати кодування файлу, який генерує користувач. Нещодавно я "створив" файл із кластеру Hadoop за допомогою Hive і передав його на FTP перед завантаженням на різні клієнтські машини. Результат мав у своєму розпорядженні сміття unicode, але я не знаю, який крок створив проблему. Я ніколи прямо не вказував кодування. Я хотів би, щоб я міг перевірити кодування на кожному кроці.
генеорама

31

Якщо ви хочете виявити кодування, що не належать до UTF (тобто немає BOM), ви в основному переходите до евристики та статистичного аналізу тексту. Можливо, ви захочете поглянути на документ Mozilla про універсальне виявлення шаблонів (те саме посилання, з кращим форматуванням через Wayback Machine ).


9
Як не дивно, моя установка Firefox 3.05 виявляє цю сторінку як UTF-8, показуючи ряд гліфів-знаків питання-в-алмазі, хоча джерело має метатег для Windows-1252. Змінення кодування символів вручну відображає документ правильно.
devstuff

5
Ваше речення "Якщо ви хочете виявити кодування, що не належать до UTF (тобто немає BOM)", є дещо оманливим; стандарт unicode не рекомендує додавати BOM до документів utf-8! (і ця рекомендація або її відсутність є джерелом багатьох головних болів). ref: en.wikipedia.org/wiki/Byte_order_mark#UTF-8
Дао

Це робиться для того, щоб ви могли об'єднати рядки UTF-8, не накопичуючи зайвих BOM. Крім того, для UTF-8 марка порядку в байті не потрібна, на відміну, наприклад, від UTF-16.
sashoalm

26

Ви спробували порт C # для універсального детектора шарсет Mozilla

Приклад з http://code.google.com/p/ude/

public static void Main(String[] args)
{
    string filename = args[0];
    using (FileStream fs = File.OpenRead(filename)) {
        Ude.CharsetDetector cdet = new Ude.CharsetDetector();
        cdet.Feed(fs);
        cdet.DataEnd();
        if (cdet.Charset != null) {
            Console.WriteLine("Charset: {0}, confidence: {1}", 
                 cdet.Charset, cdet.Confidence);
        } else {
            Console.WriteLine("Detection failed.");
        }
    }
}    

1
Працював бездоганно для Windows-1252 типу.
seebiscuit

І як ви можете використовувати його для читання текстового файлу до рядка, використовуючи це? CharsetDetector повертає ім'я кодування у строковому форматі, і ось усе ...
Bartosz

@Bartosz private Encoding GetEncodingFromString(string encoding) { try { return Encoding.GetEncoding(encoding); } catch { return Encoding.ASCII; } }
PrivatePyle

15

Ви не можете виявити кодову сторінку

Це явно помилково. Кожен веб-браузер має якийсь універсальний детектор шаблонів для роботи зі сторінками, які не мають жодної ознаки кодування. У Firefox є один. Ви можете завантажити код і подивитися, як він це робить. Дивіться деяку документацію тут . В основному це евристика, але така, яка працює дуже добре.

Враховуючи розумну кількість тексту, можна навіть виявити мову.

Ось ще один я знайшов за допомогою Google:


39
"евристика" - так що браузер не дуже його виявляє, він робить здогадну здогадку. "працює дуже добре" - значить, це не працює весь час? Мені звучить так, ніби ми згодні.
СП.

10
Стандарт для HTML диктує, що якщо набір символів не визначений документом, то його слід вважати кодованим як UTF-8.
Джон Траунтвейн

5
Що здорово, якщо ми не читаємо нестандартні документи HTML. Або документи без HTML.
Кос

2
Ця відповідь є неправильною, тому мені довелося подати заяву. Сказати, що було неправдивим, що ви не можете виявити кодову сторінку, неправильно. Ви можете здогадуватися, і ваші здогадки можуть бути досить хорошими, але ви не можете "виявити" сторінку коду.
z80crew

1
@JonTrauntvein Відповідно до специфікацій HTML5 a character encoding declaration is required even if the encoding is US-ASCII - відсутність декларації призводить до використання евристичного алгоритму, а не до повернення до UTF8.
z80crew

9

Я знаю, що це питання дуже пізно, і це рішення не сподобається деяким (через його зміст, орієнтований на англійську мову та відсутність статистичного / емпіричного тестування), але для мене це дуже добре працює, особливо для обробки завантажених даних CSV:

http://www.architectshack.com/TextFileEncodingDetector.ashx

Переваги:

  • Вбудований BOM-виявлення
  • Кодування за замовчуванням / резервне кодування, що настроюється
  • досить надійний (на мій досвід) файли на основі західноєвропейських європейських країн, що містять деякі екзотичні дані (наприклад, французькі назви) із сумішшю файлів у стилі UTF-8 та латинських 1 - в основному основна частина середовищ США та Західної Європи.

Примітка: я написав цей клас, тому явно візьміть його із зерном солі! :)



7

Шукаючи іншого рішення, я знайшов це

https://code.google.com/p/ude/

це рішення якось важке.

Мені знадобилося базове виявлення кодування на основі 4-х перших байтів і, ймовірно, виявлення набору шаблонів xml - тому я взяв з Інтернету деякий зразок вихідного коду та додав трохи змінену версію

http://lists.w3.org/Archives/Public/www-validator/2002Aug/0084.html

написаний для Java.

    public static Encoding DetectEncoding(byte[] fileContent)
    {
        if (fileContent == null)
            throw new ArgumentNullException();

        if (fileContent.Length < 2)
            return Encoding.ASCII;      // Default fallback

        if (fileContent[0] == 0xff
            && fileContent[1] == 0xfe
            && (fileContent.Length < 4
                || fileContent[2] != 0
                || fileContent[3] != 0
                )
            )
            return Encoding.Unicode;

        if (fileContent[0] == 0xfe
            && fileContent[1] == 0xff
            )
            return Encoding.BigEndianUnicode;

        if (fileContent.Length < 3)
            return null;

        if (fileContent[0] == 0xef && fileContent[1] == 0xbb && fileContent[2] == 0xbf)
            return Encoding.UTF8;

        if (fileContent[0] == 0x2b && fileContent[1] == 0x2f && fileContent[2] == 0x76)
            return Encoding.UTF7;

        if (fileContent.Length < 4)
            return null;

        if (fileContent[0] == 0xff && fileContent[1] == 0xfe && fileContent[2] == 0 && fileContent[3] == 0)
            return Encoding.UTF32;

        if (fileContent[0] == 0 && fileContent[1] == 0 && fileContent[2] == 0xfe && fileContent[3] == 0xff)
            return Encoding.GetEncoding(12001);

        String probe;
        int len = fileContent.Length;

        if( fileContent.Length >= 128 ) len = 128;
        probe = Encoding.ASCII.GetString(fileContent, 0, len);

        MatchCollection mc = Regex.Matches(probe, "^<\\?xml[^<>]*encoding[ \\t\\n\\r]?=[\\t\\n\\r]?['\"]([A-Za-z]([A-Za-z0-9._]|-)*)", RegexOptions.Singleline);
        // Add '[0].Groups[1].Value' to the end to test regex

        if( mc.Count == 1 && mc[0].Groups.Count >= 2 )
        {
            // Typically picks up 'UTF-8' string
            Encoding enc = null;

            try {
                enc = Encoding.GetEncoding( mc[0].Groups[1].Value );
            }catch (Exception ) { }

            if( enc != null )
                return enc;
        }

        return Encoding.ASCII;      // Default fallback
    }

Досить прочитати, мабуть, перші 1024 байти з файлу, але я завантажую цілий файл.


7

Якщо хтось шукає 93,9% рішення. Це працює для мене:

public static class StreamExtension
{
    /// <summary>
    /// Convert the content to a string.
    /// </summary>
    /// <param name="stream">The stream.</param>
    /// <returns></returns>
    public static string ReadAsString(this Stream stream)
    {
        var startPosition = stream.Position;
        try
        {
            // 1. Check for a BOM
            // 2. or try with UTF-8. The most (86.3%) used encoding. Visit: http://w3techs.com/technologies/overview/character_encoding/all/
            var streamReader = new StreamReader(stream, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true), detectEncodingFromByteOrderMarks: true);
            return streamReader.ReadToEnd();
        }
        catch (DecoderFallbackException ex)
        {
            stream.Position = startPosition;

            // 3. The second most (6.7%) used encoding is ISO-8859-1. So use Windows-1252 (0.9%, also know as ANSI), which is a superset of ISO-8859-1.
            var streamReader = new StreamReader(stream, Encoding.GetEncoding(1252));
            return streamReader.ReadToEnd();
        }
    }
}

Дуже приємне рішення. Можна легко обернути тіло ReadAsString () у циклі дозволених кодувань, якщо дозволено більше 2 кодувань (UTF-8 та ASCI 1252).
ViRuSTriNiTy

Спробувавши багато прикладів, я нарешті дістався до ваших. Я зараз у щасливому місці. лол дякую !!!!!!!
Седрік

Це може бути не відповіддю на те, як виявити 1252 проти 1250, але це абсолютно має бути відповіддю для "Як виявити UTF-8" з BOM або без нього !!
Чак

4

Я щось подібне зробив у Python. В основному, вам потрібно безліч зразкових даних з різних кодувань, які розбиваються на розсувне двобайтне вікно і зберігаються в словнику (хеш), введеному на байтові пари, що надають значення списків кодувань.

Враховуючи цей словник (хеш), ви берете свій вхідний текст і:

  • якщо він починається з будь-якого символу BOM ('\ xfe \ xff' для UTF-16-BE, '\ xff \ xfe' для UTF-16-LE, '\ xef \ xbb \ xbf' для UTF-8 тощо), я ставитися до цього, як пропонується
  • якщо ні, то візьміть досить великий зразок тексту, візьміть усі байт-пари зразка та виберіть кодування, яке є найменш запропонованим із словника.

Якщо ви також відібрали тексти, закодовані UTF, які не починаються з жодної BOM, другий крок охоплює ті, що прослизнули з першого кроку.

Поки що це працює для мене (вибіркові дані та наступні вхідні дані є субтитрами на різних мовах) зі зменшенням рівня помилок.


4

Інструмент "учардет" робить це добре, використовуючи моделі розподілу частоти символів для кожного шаблону. Більш великі та "типові" файли мають більшу впевненість (очевидно).

У ubuntu ви просто apt-get install uchardet.

В інших системах знайдіть джерело, використання та документи тут: https://github.com/BYVoid/uchardet


На Mac через домашню мову:brew install uchardet
Пол Б

3

Конструктор класу StreamReader приймає параметр «виявити кодування».


Тут просто посилання на "кодування" . І в описі йдеться про те, що ми повинні надати кодування ..
SurajS

@SurajS: Подивіться на інші перевантаження.
леппі

оригінальний автор хоче виявити кодування для файлу, в якому потенційно б не було маркера BOM. StreamReader виявляє кодування з заголовка BOM відповідно до підпису. public StreamReader (Stream stream, bool detectEncodingFromByteOrderMarks)
ibondre

1

Якщо ви можете зв’язатись із бібліотекою С, ви можете використовувати libenca. Дивіться http://cihar.com/software/enca/ . Із чоловічої сторінки:

Enca читає задані текстові файли або стандартне введення, коли жодне не дано, і використовує знання про їхню мову (повинні бути підтримані вами) та суміш розбору, статистичного аналізу, здогадів та чорної магії для визначення їх кодування.

Це GPL v2.


0

Отримала таку ж проблему, але поки не знайшла хорошого рішення для автоматичного її виявлення. Зараз я використовую для цього PsPad (www.pspad.com);) Відмінно працює


0

Оскільки це в основному зводиться до евристики, це може допомогти використовувати кодування раніше отриманих файлів з того самого джерела як перший підказку.

Більшість людей (або додатків) роблять речі в майже однаковому порядку кожен раз, часто на одній машині, тому цілком ймовірно, що коли Боб створює .csv файл і надсилає його Марії, він завжди буде використовувати Windows-1252 або незалежно від його машини.

Де можливо, трохи навчання клієнтів ніколи не шкодить :-)


0

Я насправді шукав загальний, а не програмний спосіб виявлення кодування файлів, але цього ще не знайшов. Що я знайшов, тестуючи різні кодування, це те, що мій текст був UTF-7.

Тож де я вперше робив: файл StreamReader = File.OpenText (повне ім’я);

Мені довелося змінити його на: StreamReader file = new StreamReader (повне ім'я файлу, System.Text.Encoding.UTF7);

OpenText передбачає, що це UTF-8.

Ви також можете створити StreamReader, як цей новий StreamReader (повне ім'я файлу, правда), другий параметр означає, що він повинен спробувати виявити кодування з байтардного позначення файлу, але це не спрацювало в моєму випадку.


@JohnMachin Я погоджуюся, що це рідко, але це передбачено, наприклад, у деяких частинах протоколу IMAP. Якщо саме там ви знаходитесь, вам не доведеться здогадуватися.
трійка

0

Відкрийте файл в AkelPad (або просто скопіюйте / вставте зібраний текст), перейдіть до меню Правка -> Виділення -> Перекодувати ... -> встановіть прапорець "Автоматично виявити".


0

Як доповнення до публікації ITmeze, я використовував цю функцію для перетворення виходу C # порту для Mozilla Universal Charset Detector

    private Encoding GetEncodingFromString(string codePageName)
    {
        try
        {
            return Encoding.GetEncoding(codePageName);
        }
        catch
        {
            return Encoding.ASCII;
        }
    }

MSDN


0

Дякуємо @ Еріку Аронесті за згадування uchardet.

Тим часом (той самий?) Інструмент існує для linux : chardet.
Або, на Cygwin ви можете використовувати: chardetect.

Дивіться: сторінка chardet man: https://www.commandlinux.com/man-page/man1/chardetect.1.html

Це дозволить евристично виявити (відгадати) кодування символів для кожного файлу та повідомить ім’я та рівень довіри для кодованого символу кожного виявленого файлу.


-1

Я використовую цей код для виявлення кодової сторінки ансі за замовчуванням Unicode та Windows під час читання файлу. Для інших кодувань потрібно перевірити вміст вручну або за допомогою програмування. Це можна використовувати для збереження тексту з тим же кодуванням, що і при його відкритті. (Я використовую VB.NET)

'Works for Default and unicode (auto detect)
Dim mystreamreader As New StreamReader(LocalFileName, Encoding.Default) 
MyEditTextBox.Text = mystreamreader.ReadToEnd()
Debug.Print(mystreamreader.CurrentEncoding.CodePage) 'Autodetected encoding
mystreamreader.Close()

-1

10Y (!) Минуло відтоді, як це було запропоновано, і досі я не бачу жодної згадки про хороше, не GPL'ed рішення MS : API IMultiLanguage2 .

Більшість уже згаданих бібліотек базуються на UDE Mozilla - і здається, що браузери вже вирішили подібні проблеми. Я не знаю, що таке рішення хрому, але оскільки IE 5.0 MS випустив своє, і це:

  1. Без ліцензійних проблем GPL та подібних,
  2. Підтримується та підтримується, ймовірно, назавжди,
  3. Дає багатий вихід - всі дійсні кандидати для кодування / кодових сторінок разом із оцінками достовірності,
  4. Дивно просто у використанні (це один дзвінок функції).

Це рідний COM-дзвінок, але ось дещо дуже приємна робота Карстена Зюмера, яка обробляє безлад інтеропу для використання .net. Навколо є деякі інші, але за великим рахунком ця бібліотека не отримує тієї уваги, якої вона заслуговує.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.