lunedì 4 giugno 2012

Unicode e IFS


Dopo essermi cimentato con caratteri unicode (Cinese, Giapponese e Coreano) a livello di database, riuscendo a leggere, scrivere e garantendo il round-trip tra diverse applicazioni e middelware, mi son trovato a dover leggere e scrivere stream files: scambiare con applicazioni windows i dati presenti sul database.

La sfida dunque era esportare dati verso un file di testo (.txt .csv) in modalità Unicode, Big Endian Unicode  o UTF-8, leggere files analoghi (risultanti da upload effettuati dall’estero), capire come riconoscere il loro formato ed importarli in tabelle.

Windows fornisce il supporto ai formati unicode (la Fig. 1 mostra le opzioni di salvataggio di Notepad); vediamo in dettaglio i diversi formati supportati

ANSI - non è unicode, rappresenta il character set ASCII
Unicode / Big-endian Unicode - Quando un carattere occupa più di un byte può essere scritto in due modalità, dipendente dall’architettura della macchina;  col termine Unicode Windows intende una codifica a 2 byte (quindi l’equivalente di UCS-2 o UTF-16), differenziando (BE, LE) la posizione dei 2 byte occorrenti all’identificazione di un carattere
UTF-8 - Questo Encoding prevede l’utilizzo di uno, due o più byte dipendentemente dal carattere: sono i primi bit del primo carattere che ne determinano la lunghezza. E’ quindi un sistema ottimizzato per occupare poco spazio (solo quello che serve) a discapito delle prestazioni.

Vediamo ora come viene occupata la memoria per scrivere la stringa “CINA” nei diversi Encoding:
Risulta evidente che i caratteri usati dalla codifica ANSI e quelli utilizzati dalla codifica UTF-8 in questo caso sono identici: la parte “bassa” di unicode, quella compresa da 0016 a 7F16 corrisponde ai primi 127 caratteri della codifica ISO 8859-1 Latin 1: utilizzare quindi UTF-8 risulta la miglior scelta qualora si debbano rappresentare caratteri europei. Si nota anche la sostanziale differenza tra i due Encoding di Unicode (LE e BE): nel primo (LE) il byte meno importante viene per primo, nel secondo (BE) il byte meno importante viene per secondo.

 
Proviamo ora a vedere come vengono rappresentati dei caratteri cinesi; la coppia中国 (zhong-guo) non può essere rappresentato con una codifica ANSI; in Unicode  (sia LE che BE) occupa 2 caratteri, per un totale di 4 byte. Nell’Encoding UTF-8 occupa 6 byte (vedi figura). Sicuramente UTF-8 e’ in grado di rappresentare tutti i caratteri esistenti (essendo una codifica multi - byte a lunghezza variabile) ma soffre di una certa verbosità quando deve rappresentare caratteri non europei, in particolare asiatici.
 
Su system-i è possibile attribuire ad una singola colonna di tabella un determinato CCSID che determina l’encoding usato; questa caratteristica permette quindi al sistema di conoscere e trattare opportunamente le informazioni; nella figura un esempio dei CCSID corrispondenti ad alcuni Encoding.

 
Nel mondo Windows la codepage è determinata dall’installazione del sistema operativo (CP819 corrisponde a ISO8852-1 ASCII oppure CP1252 corrisponde a  MS Windows Latin 1); riprendendo l’esempio precedente, cosa succederebbe se visualizzassimo le sequenze di byte senza però poter istruire il sistema circa l’Encoding usato? Pura spazzatura: il sistema cerca di far corrispondere ad ogni byte il corrispondente carattere ASCII;
è necessario quindi, per i file di testo (e per tutti i flussi non caratterizzati implicitamente da un CCSID)  un ulteriore metodo per riconoscere l’Encoding usato, per preservare quindi le applicazioni legacy e permettere un corretto uso di unicode.

BOM

Il BOM (byte order mark) consiste in una serie di caratteri all’inizio del flusso dei dati, usato come firma per definire il byte order (LE e BE) e il tipo di Encoding (UTF8/16/32); potete verificare il BOM salvando un qualsiasi file di testo nelle diverse modalità previste da Notepad e visualizzando poi il file con un editor esadecimale.



 

Scriviamo ora un file nell’ IFS

Alla luce di quanto detto risulta estremamente semplice scrivere uno streamfile con formato UTF-8 nell’ integrated file system di system-i.
Per prima cosa occorre creare un file vuoto con la giusta codepage:
D LogFile         S            640a              
D flags           S             10u 0            
D mode            S             10u 0            
D fd              S             10i 0            
D codepage        S             10u 0            

D O_WRONLY        C                   2     
D O_CREAT         C                   8     
D O_TRUNC         C                   64    
D O_RDONLY        C                   1     
D O_RDWR          C                   4     
D O_APPEND        C                   256   
D O_CODEPAGE      C                   8388608

LogFile = '/Temp/testUTF8.txt';
flags =  O_RDONLY;
if open(%trim(LogFile):flags: mode)<0;
// il file NON Esiste, lo creo
    flags = O_CREAT  + O_RDWR + O_CODEPAGE + O_TRUNC ;
    mode = S_IRUSR  + S_IWUSR + S_IRGRP + S_IROTH;
    codepage = 819;
    fd = open(%trim(LogFile):flags: mode: codepage);
    // open Output.
    if fd >= 0;
        fclose(fd);
    endif;
endif;

 
ora aggiungiamo il BOM dell’UTF-8, per far riconoscere l’encoding usato a windows
D BOM             S              3a   inz(x'EFBBBF')       

flags = O_APPEND + O_RDWR;                                  
mode  = S_IRUSR  + S_IWUSR + S_IRGRP + S_IROTH;              
fd = open(%trim(LogFile):flags: mode);                      
if fd >= 0;                                                 
   // write to stream file                                  
   fwrite(fd: %addr(BOM): 3);  // per UTF-8: Byte Order Mark
   fclose(fd);     
endif;                                         

Prepariamo un campo unicode direttamente; possiamo usare la bif %UCS2 per convertire una stringa SBCS in un campo Unicode oppure la notazione U’xxxx’ per generare valori unicode:

D Message2        S          10000C

Message2 =%ucs2('[')+U'4E2D56FD' +%ucs2(']')     
                  +U'000D000A';    
 
In questo esempio viene generata la stringa [中国] seguita da CR+LF (0D16 e 0A16 che nella codifica a 2 byte diventano U’000D’ e U’000A’)

Va ora considerato che un campo Unicode, un campo di tipo C nelle D-spec, è un campo con CCSID 13488 quindi UCS-2; serve ora convertirlo in UTF-8; la strada più semplice è quella di usare una funzione del sistema: iconv() che, a fronte di una stringa di input, ccsid di partenza e di arrivo, genera una stringa di output; questa funzione è stata incapsulata in una subprocedure; in questo caso passiamo alla funzione iconv la stringa da convertire, la sua lunghezza, il suo CCSID (13488) e il CCSID nel quale vogliamo venga restituita la stringa calcolata (1208 = UTF-8):

D Ou              S          10000a


fd = open(%trim(LogFile):flags: mode);                             
if fd >= 0;                                                        
   Ou = my_iconv(Message2:%len(%trim(Message2)):13488:1208);
   fwrite(fd: %addr(Ou): %len(%trim(Ou)));                         
   fclose(fd);                                                     
endif;                   

alla stregua di questo si può leggere una tabella codificata in Unicode per scrivere il suo contenuto nello streamfile:

for $ii=1 to 10;                                                  
   read  MyFile;                                                  
   if %eof;                                                      
      leave;                                                     
   endif;                                                         
   fd = open(%trim(LogFile):flags: mode);                        
   if fd >= 0;                                                   
      Message2 = %ucs2(%trim(MyField))                                        
               + U'000D000A';                                         
      Ou = my_iconv
             (Message2:
              %len(%trim(Message2)):
              13488:
              1208);
      fwrite(fd: %addr(Ou): %len(%trim(Ou)));                    
      fclose(fd);                                                 
   endif;                                                        
endfor;                                                          

Verifichiamo ora se tutto ha funzionato come desiderato: dobbiamo verificare il CCSID del file che abbiamo appena generato, la corretta scrittura del BOM e la sua leggibilità da parte di un’applicazione windows.
Col comando WRKLNK navighiamo nell’IFS e troviamo il nostro file:
 

 Con l’opzione 8=Attributi verifichiamo il CCSID e con EDTF in modalità HEX verifichiamo il BOM:




 
Esportiamo ora il file su un client Windows (si può usare qualsiasi sistema, da iSeriesNavigator a FTP o da SharedFolders).
Apriamo il file con Notepad, dovremmo vedere questo contenuto:


 Se lanciamo il comando [Salva con nome…] notepad visualizza la modalità di salvataggio corrispondente all’Encoding corrente, nel nostro caso UTF-8








Nessun commento:

Posta un commento