Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Как распознать UTF 8 
:(
    Опции темы
livo
Дата 2.5.2012, 15:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 14.10.2007

Репутация: 1
Всего: 1



Доброго времени!
Есть файл, в котором записана текстовая информация. Вопрос: как узнать в какой кодировке (ANSI, UTF8) записана информация, чтобы корректно ее прочитать и вывести?
PM MAIL   Вверх
Alexeis
Дата 2.5.2012, 15:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 26
Всего: 459



Часто файлы UTF8 содержат BOM . По нему можно определить. Если же нету BOM, и скажем английский текст в файле, то он будет совпадать с ANSI по содержимому и будет неотличим.

Добавлено через 2 минуты и 19 секунд
  Кроме того правильные текстовые файлы должны описывать свою кодировку. Например XML файлы имеют специальный атрибут для описания кодировки. 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
livo
Дата 2.5.2012, 15:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 14.10.2007

Репутация: 1
Всего: 1



Но файл именно текстовый и без BOM.
PM MAIL   Вверх
Alexeis
Дата 2.5.2012, 16:51 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 26
Всего: 459



Цитата(livo @  2.5.2012,  16:53 Найти цитируемый пост)
Но файл именно текстовый и без BOM. 

  Гарантированного способа нет. Можно вероятностно по статистике символов.


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
livo
Дата 3.5.2012, 11:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 14.10.2007

Репутация: 1
Всего: 1



Я так посмотрел на файлы, сохраненные в разных кодировках, и увидел, что в UTF8 латинские символы хранятся в одном байте и имеют один код по сравнению с ANSI. В то время как русские символы хранятся в двух байтах и имеют другой код. Из этого выходит вывод - файл с латиницей можно открывать в любой кодировке. Распознать UTF8 можно, если в файле есть кириллица. Только вот как определить текущий байт: является ли он автономным или же он часть группы из двух байтов?
PM MAIL   Вверх
xvr
Дата 3.5.2012, 12:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 48
Всего: 223



Цитата(livo @  3.5.2012,  11:32 Найти цитируемый пост)
Только вот как определить текущий байт: является ли он автономным или же он часть группы из двух байтов? 

Для UTF8 все байты, входящие в многобайтовые последовательности (да, там могут быть и более чем 2 байта на Unicode символ) имеют единичный старший бит (т.е. коды 0x80-0xFF)

PM MAIL   Вверх
Alexeis
Дата 3.5.2012, 12:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 26
Всего: 459



Цитата(livo @  3.5.2012,  12:32 Найти цитируемый пост)
Распознать UTF8 можно, если в файле есть кириллица. Только вот как определить текущий байт: является ли он автономным или же он часть группы из двух байтов? 

  Не только кириллицей отличаются. Совпадают первые 127 символов.
  Правило такое. Если это UTF8, то символы должны соответствовать правилам из таблички http://ru.wikipedia.org/wiki/UTF-8
Нужно прочитать все символы и проверить на верность. Если хоть 1 символ не про правилам записан, то это ANSI, хотя теоретически можно написать такой текст, который будет распознаваться как UTF-8 но будет на самом деле ANSI.



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
livo
Дата 4.5.2012, 12:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 14.10.2007

Репутация: 1
Всего: 1



Цитата(xvr @  3.5.2012,  12:05 Найти цитируемый пост)
Для UTF8 все байты, входящие в многобайтовые последовательности (да, там могут быть и более чем 2 байта на Unicode символ) имеют единичный старший бит (т.е. коды 0x80-0xFF)

Так, но не  совсем. Младшие биты тоже могут быть единичными:
Цитата(Alexeis @  3.5.2012,  12:14 Найти цитируемый пост)
http://ru.wikipedia.org/wiki/UTF-8

Вот написал функцию, которая должна распознавать файл по указанному пути:
Код

bool IsUTF8(String fn)
{
 TFileStream *fs=0;
 std::string buff;
 unsigned codes[3][4]={
                        0xC0,0x80,0x00,0x00,//значимы два первых элемента; кириллица, расширенная латиница, арабский, армянский, греческий, ...
                        0xE0,0x80,0x80,0x00,//значимы три первых элемента; грузинский алфавит, индийское, китайское, корейское письмо, ...
                        0xF0,0x80,0x80,0x80//значимы все элементы; музыкальные символы, редкие китайские иероглифы, вымершие формы письменности
                      },
          masks[3][4]={//маски для байтов
                        0xE0,0x80,0x00,0x00,
                        0xF0,0x80,0x80,0x00,
                        0xF8,0x80,0x80,0x80
                      };
 bool utf8=0,
      id;
 try
 {
   fs=new TFileStream(fn,fmOpenRead|fmShareDenyWrite);
   buff.resize(fs->Size);
   fs->ReadBuffer(&buff[0],fs->Size);
   for(int i=0; !utf8 && i<buff.size(); ++i)//проходим по прочитанному массиву и побайтово сверяемся с кодами UTF8
     for(int j=0; j<3; ++j)//проход по кодам
       if(i+j+1>=buff.size())break;//если вылезаем за размерность прочитанного массива, то прекращаем  проход
       else
       {//проверка текущей комбинации кодов
         id=1;
         for(int m=0; m<j+2; ++m)
           if(buff[i+j] & masks[j][m] != codes[j][m])
           {
             id=0;
             break;
           }
         if(id)utf8=1;//если соответствие есть, то отмечаем флаг
       }
 }
 catch(...)
 {}
 if(fs)delete fs;
 return utf8;
}


Но вот не пашет чего-то. Где баг?
PM MAIL   Вверх
Alexeis
Дата 4.5.2012, 13:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 26
Всего: 459



Цитата(livo @  4.5.2012,  13:32 Найти цитируемый пост)
Но вот не пашет чего-то. Где баг? 

  инкремент переменной i . Если мы прошли 2-4 байта, то нужно смещаться соответственно на 2-4 байта, а не на 1


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
livo
Дата 5.5.2012, 11:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 14.10.2007

Репутация: 1
Всего: 1



Цитата(Alexeis @  4.5.2012,  13:20 Найти цитируемый пост)
инкремент переменной i . Если мы прошли 2-4 байта, то нужно смещаться соответственно на 2-4 байта, а не на 1

Нет, в моей функции другая логика предполагалась: нужно было найти всего одно совпадение порядка следования байтов, характерного для UTF8, чтобы в итоге сказать, что файл именно в такой кодировке. Кстати, как думаете, корректен ли такой подход?

Я несколько изменил функцию; она работает, но если кто что заметит, буду благодарен за сообщение.
Код

bool IsUTF8(String fn)
{
 TFileStream *fs=0;
 std::string buff;
 char codes[3]={0xC0,0xE0,0xF0},
      masks[3]={0xE0,0xF0,0xF8};
 bool utf8=0,
      id;
 try
 {
   fs=new TFileStream(fn,fmOpenRead|fmShareDenyWrite);
   buff.resize(fs->Size);
   fs->ReadBuffer(&buff[0],fs->Size);
   for(int i=0; !utf8 && i<buff.size(); ++i)
     for(int c=0; c<3; ++c)
       if(i+c+1<buff.size() && (buff[i] & masks[c]) == codes[c])
       {
         id=1;
         for(int p=1; p<c+2; ++p)
           if( (buff[i+p] & 0xC0)!=0x80 )
           {
             id=0;
             break;
           }
         if(id)
         {
           utf8=1;
           break;
         }
       }
 }
 catch(...)
 {}
 if(fs)delete fs;
 return utf8;
}

PM MAIL   Вверх
xvr
Дата 5.5.2012, 12:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 48
Всего: 223



Цитата(livo @  4.5.2012,  12:32 Найти цитируемый пост)
Так, но не  совсем. Младшие биты тоже могут быть единичными:

Ну и что? Старшие то обязаны быть единичными.

Цитата(livo @  5.5.2012,  11:43 Найти цитируемый пост)
Нет, в моей функции другая логика предполагалась: нужно было найти всего одно совпадение порядка следования байтов, характерного для UTF8, чтобы в итоге сказать, что файл именно в такой кодировке.

Одного совпадения не достаточно. 
В принципе не сущеcтвует способа отличить файл в UTF8 от файла в любой другой однобайтовой CP (точнее, всегда найдется последовательность, которая будет валидна и как UTF8 и как однобайтовая CP, например cp1255)

Максимум можно просканировать весь файл и убедится, что с точки зрения кодирования UTF8 он правильный (это кстати будет так и для любого чисто английского текста).
Минимум можно найти в файле любой байт с старшим битом, и сказать, что это UTF8
Ваш подход - это нечто среднее

Кстати, функцию можно сделать проще
Код

bool is_utf(FILE* f, bool min_check)
{
  int c,count=0;
  while( (c=fgetc(f)) != EOF)
   if (count)
    {
      if ((c&0xC0) != 0x80) return false;
      --count;
      if (!count && min_check) return true;
    }
   else if ( (c&0xE0) == 0xC0) count=1;
   else if ( (c&0xF0) == 0xE0) count=2;
   else if ( (c&0xF8) == 0xF0) count=3;
   else if (c&0x80) return false;
 return count==0;
}

PM MAIL   Вверх
livo
Дата 12.5.2012, 12:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 14.10.2007

Репутация: 1
Всего: 1



Я согласен, что нужно прогнать по всему файлу, чтобы составить представление о кодировке, так как моя функция анси-файл распознает как ютф. Не все файлы, но был встречен такой. 
xvr, спасибо за оптимизацию; изменил свою функцию в соответствии с вашим кодом:
Код

bool is_utf(String fn)
{
 TFileStream *fs=0;
 std::string buff;
 int count=0;
 try
 {
   fs=new TFileStream(fn,fmOpenRead|fmShareDenyWrite);
   buff.resize(fs->Size);
   fs->ReadBuffer(&buff[0],fs->Size);
   delete fs;
   for(int i=0; i<buff.size(); ++i)
     if (count)
     {
        if ((buff[i]&0xC0) != 0x80) return false;
        --count;
     }
     else if ( (buff[i]&0xE0) == 0xC0) count=1;
     else if ( (buff[i]&0xF0) == 0xE0) count=2;
     else if ( (buff[i]&0xF8) == 0xF0) count=3;
     else if (buff[i]&0x80) return false;
 }
 catch(...)
 {
   count=-1;
   if(fs)delete fs;
 } 
 return count==0;
}


Это сообщение отредактировал(а) livo - 12.5.2012, 12:34
PM MAIL   Вверх
artsb
Дата 12.5.2012, 13:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 2280
Регистрация: 17.7.2007
Где: центр Вселенной

Репутация: 39
Всего: 64



Ещё хорошо бы было сначала проверить файл на наличие BOM, а уже потом его анализировать.


--------------------
Чем отличается умный человек от мудрого?
Умный - выпутается из любой ситуации.
Мудрый - просто в неё не попадёт.
PM MAIL   Вверх
xvr
Дата 12.5.2012, 13:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 48
Всего: 223



Цитата(artsb @ 12.5.2012,  13:03)
Ещё хорошо бы было сначала проверить файл на наличие BOM, а уже потом его анализировать.

Бывают UTF8 файлы без BOM (равно как и Unicode без него же)

PM MAIL   Вверх
livo
Дата 12.5.2012, 18:52 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 104
Регистрация: 14.10.2007

Репутация: 1
Всего: 1



Цитата(artsb @  12.5.2012,  13:03 Найти цитируемый пост)
Ещё хорошо бы было сначала проверить файл на наличие BOM

Да, согласен. Только вот почему-то не распознается у меня второй байт триады маркера.
Код

//...
if(buff.size()>=3 && buff[0]==0xEF && buff[1]==0xBB && buff[2]==0xBF)return 1;
//...

Хотя в том же Листере видно код BB.
PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++ Builder"
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по С++ Builder обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Настоятельно рекомендуем заглянуть в DRKB (Delphi Russian Knowledge Base) - крупнейший в рунете сборник материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C++ Builder | Следующая тема »


 




[ Время генерации скрипта: 0.1240 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.