Karakterkódolás felismerése egy szöveges állományban

Ma (igazából tegnap, de ma kellett rá végleges megoldás) belefutottunk abba a problémába, hogy egy kapott CSV állományról el kellett dönteni, hogy milyen kódolással és milyen sorvége jelekkel lett elkészítve.

A szóba jöhető karakterkódolások száma szerencsére eléggé limitált: Windows 1250 vagy UTF-8. Ezeket megvizsgálva arra jutottam, hogy lehetséges ilyen algoritmust készíteni, mert normál magyar karaktereket feltételezve UTF-8 kódolással 0xFA és 0xFC közötti karakterek nem jöhetnek, viszont Windows 1250-ben ezek az 'ú', 'ü', 'ű' karaketeket jelentik (nem ebben a sorrendben). Ugyanígy, UTF-8-ban egy ékezetes karakternél az első bájt értéke két 1-es és egy 0 bittel fog kezdődni (0b110xxxxx) és a következő bájt egy 1-es és egy 0 bittel kezdődik (0b10xxxxxx). Ezek alapján készült az alábbi egyszerű C forrás, hogy ezeket felismerje:

  #include <stdlib.h>
  #include <stdio.h>
  #include <fcntl.h>
  #include <sys/types.h>

  void CheckFileContents( char *filename, int *ccsid, int *lineterm )
  {
     char *buffer;
     int f;
     int bytes;
     int i;
     int currchar, prevchar;

     *ccsid = 0;
     *lineterm = 0;
     prevchar = -1;
     buffer = (char * ) malloc( 2048 );

     f = open( filename, O_RDONLY, 0 );
     if( f >= 0 ) {
       bytes = read( f, buffer, 2048 );
       while( bytes > 0 && ( *ccsid == 0 || *lineterm == 0 ) ) {
         for( i = 0; i < bytes ; i ++ ) {
           currchar = (int) buffer[i];
           if( *ccsid == 0 ) {
             if( ( prevchar & 0xE0 ) == 0xC0 &&
                 ( currchar & 0xC0 ) == 0x80 ) {
               *ccsid = 1208;
             }
             if( ( currchar >= 0xFA ) && ( currchar <= 0xFC ) ) {
               *ccsid = 1250;
             }
           }
           if( *lineterm == 0 ) {
             if( prevchar == 0x0A && currchar != 0x0D ) {
               *lineterm = 1;
             }
             if( prevchar == 0x0D && currchar != 0x0A ) {
               *lineterm = 2;
             }
             if( prevchar == 0x0D && currchar == 0x0A ) {
               *lineterm = 3;
             }
             if( prevchar == 0x0A && currchar == 0x0D ) {
               *lineterm = 4;
             }
           }
           prevchar = currchar;
         }
         bytes = read( f, buffer, 2048 );
       }
       close( f );
     }

     free( buffer );

     return;
  }

  int main( void ) {
    int ccsid, lineterm;
    char lt[5][4] = { "????", "LF  ", "CR  ", "CRLF", "LFCR" };
    CheckFileContents( "/home/iszell/csv/2010_FELH_20170308_1.csv",
                       &ccsid, &lineterm );
    printf("CCSID: %d, line terminator code: %.4s\n", ccsid, lt[lineterm]);
  }