Programowanie w języku C/C++ » FAQ

Rozpoznawanie standardu polskich znaków

Kiedyś dawno temu, kiedy era komputeryzacji w naszym kraju się dopiero rozpoczynała, kiedy królowały takie karty graficzne jak hercules i cga, a ega była przeznaczona dla stacji graficznych nie istniał żaden ogólnie przyjęty sposób kodowania polskich znaków. W czasopismach w owym czasie pojawiały się często artykuły mające w tytule aluzje do polaków i gęsi.. No i powstało wtedy kilka róznych kodowań polskiego alfabetu (mazovia, cyfromat, latin2, DHN, CSK, ...) I powstał rowniez problem... Jak rozpoznać kodowanie pl-literek ?

Stworzyłem więc sobie (jeszcze w pascalu) funkcje, ktora zwracała mi rodzaj kodowania. Tak na prawdę, w przypadku nieznanego pliku nigdy do końca nie można mieć pewności, jak pl-literki zostały zakodowane, dlatego że można się natknąć na np. ascii art, bądz też zapis w innym języku. Mozna mówić jedynie o pewnym prawdopodobieństwie.

Co mnie dziś do niej z powrotem przyciągnęło ? Kilka spraw. 1) Linux/Windows. 2) Nie kodowałem tego jeszcze w c. 3) Post na forum warty stworzenia wskazówki.

Jak działa funkcja?

Częśc pierwsza zlicza gęstość występowania każdego znaku. Zmienna znaki to tablica, ktorej indeksy są kodami ascii wczytanych znaków. Suma po wszystkich jej komórkach to długość pliku :]

Częśc druga natomiast próbuje dopasować kolejne standardy do gęstości występowania i wybrać najlepszy. W najprostszy sposób. W st są kolejno zakodowane kody ascii 18-tu polskich znaków (nie musza być w jakiejkolwiek kolejności, byle byłyby wszystkie). Największa suma występowania tych znaków wskazuje nam właśnie na prawdopodobne kodowanie. Lecąc po tych znakach sumuje się ich liczbę w pliku.

Funkcja ma jedno zabezpieczenie... Otóż jesli w pliku jest po rowno znaków wskazujących na jeden, bądź drugi standard, to zwracane nie jest ostatnie dopasowanie, tylko UNKNOWN.

#define IO_ERROR  -1
#define UNKNOWN    0
#define ISO_8859_2 1
#define CP1250     2
 
int checkpolishstandard(char* file){
 
  typedef unsigned char uchar; // iniezależnic sie od ustawien kompilatora. zawsze unsigned char
 
  long znaki[256];
  memset(znaki,0,1024); // zerowanie tablicy
 
  // cz. 1 - zliczanie ilosci wystepowania kazdego kodu ascii
 
  FILE* f=fopen(file,"rb");
  if(!f)return -1;  // i/o error
  long l=filelength(fileno(f));
  while(l){
    uchar buf[1024];
    int ll=1024;
    if(ll>l)ll=l;
    if(fread(buf,ll,1,f)^1){
      fclose(f);
      return -1;  // i/o error
    }
    l-=ll;
    while(ll--)znaki[buf[ll]]++;
  }
  fclose(f);
 
  // cz. 2 - sprawdzanie standardu
 
  uchar st[2][18]=
  /*iso-8859-2*/  {{175,172,166,211,209,163,202,198,161,191,188,182,243,241,179,234,230,177},
  /*cp1250*/       {175,143,140,211,209,163,202,198,165,191,159,156,243,241,179,234,230,185}};
  int i=2;   // ilosc standardow w st[]
  int best=0; // najlepsze dopasowanie
  long max=0; // max, ilosc znakow pasujacych
  while(i--){
    l=0;
    int j=18;
    while(j--)l+=znaki[st[i][j]];
    if(l>=max){
      best=(i+1)*(l>max); // jesli l==max to nie wiadomo ktory standard... UNKNOWN, czyli 0
      max=l;
    }
  }
  return best;
}