[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home] [volume] [parte]


Capitolo 581.   C: configurazione locale

La libreria standard del linguaggio C prevede la gestione della configurazione locale, attraverso l'indicazione di una stringa da associare a una categoria, dove la categoria rappresenta il contesto particolare della configurazione locale a cui si vuole fare riferimento.

La stringa con cui si indica il tipo di configurazione desiderato, contiene le informazioni sulla lingua, la nazionalità e soprattutto la codifica da usare per la rappresentazione delle sequenze multibyte. La codifica scelta condiziona l'insieme di caratteri che possono essere gestiti, sia attraverso le sequenze multibyte, sia attraverso i caratteri estesi.

581.1   Configurazione locale nei sistemi Unix e simili

In un sistema Unix o simile, la configurazione locale viene definita impostando alcune variabili di ambiente. Si tratta precisamente di variabili il cui nome inizia per LC_..., dove in particolare la variabile LC_ALL, se usata, prende il sopravvento su tutte, mentre la variabile LANG (se LC_ALL non viene usata) serve per la configurazione predefinita di tutte le altre variabili LC_... che non fossero state dichiarate espressamente. A queste variabili di ambiente si associa una stringa secondo il formato seguente:

lingua_nazionalità.codifica

Per esempio, la configurazione de_CH.UTF-8 rappresenta la configurazione di lingua tedesca per la Svizzera, con una codifica UTF-8.

Ogni variabile di ambiente LC_..., esclusa LC_ALL, rappresenta una categoria, ovvero un contesto particolare a cui applicare la configurazione locale. Per esempio, pur volendo gestire i numeri con una rappresentazione europea (con la virgola per i decimali), si potrebbe voler gestire le valute in dollari americani. Pertanto ci potrebbe essere un uso contrastante delle variabili LC_NUMERIC e LC_MONETARY.

581.2   Configurazione locale nel linguaggio C

Il linguaggio C non gestisce la configurazione locale attraverso le variabili di ambiente, perché non è detto che il sistema in cui si trova a operare il programma le preveda. Tuttavia definisce le categorie della configurazione locale attraverso macro-variabili (dichiarate nel file locale.h) con gli stessi nomi e significati usati per le variabili di ambiente dei sistemi Unix e simili (vale anche il fatto che la macro-variabile LC_ALL si riferisca simultaneamente a tutte le categorie previste). Le macro-variabili in questione riguardano solo le categorie LC_..., mentre la variabile di ambiente LANG non ha alcun corrispondente nel linguaggio e non rappresenta precisamente una categoria, ma solo un valore predefinito.

La configurazione locale di partenza per un programma scritto in linguaggio C è proprio la configurazione C, la quale coincide sostanzialmente con la modalità di funzionamento tradizionale del linguaggio, con una codifica ASCII o equivalente. Per impostare la configurazione locale si usa la funzione setlocale() secondo il modello seguente:

char *setlocale (int categoria, const char *configurazione);

Il primo parametro è un numero intero che si indica normalmente attraverso una macro-variabile LC_...; il secondo è una stringa, contenente la definizione della configurazione, per esempio it_IT.UTF-8. Se la funzione è nelle condizioni di accettare la configurazione richiesta, restituisce un puntatore alla stringa che definisce la configurazione stessa; altrimenti dà solo il puntatore nullo.

Come accennato, all'avvio ogni programma si trova a funzionare come se fosse stata usata la configurazione C, ovvero come se fosse stata usata la funzione setlocale() così:

setlocale (LC_ALL, "C");

Per richiedere una configurazione più attuale e più utile, conviene specificare qualcosa che preveda la codifica UTF-8, con la quale è possibile rappresentare qualunque carattere della codifica universale:

setlocale (LC_ALL, "fr_CH.UTF-8");

Tuttavia, se il sistema operativo ha una gestione della configurazione locale, così come avviene nei sistemi Unix e simili, è meglio far sì che il programma erediti tale configurazione. Per ottenere questo, si usa la funzione setlocale() lasciando una stringa nulla (nel senso di vuota) al posto della configurazione richiesta:

setlocale (LC_ALL, "");

Per interrogare la configurazione locale attiva per una certa categoria (o per tutte se si fa riferimento a LC_ALL), è sufficiente fornire il puntatore nullo al posto della stringa. L'esempio seguente è completo e si vede anche l'incorporazione del file locale.h, contenente il prototipo della funzione setlocale() e la dichiarazione delle macro-variabili LC_...:

#include <stdio.h>
#include <locale.h>
int main (int argc, char *argv[])
{
    setlocale (LC_ALL, "");
    char *loc;
    loc = setlocale (LC_ALL, NULL);
    printf ("LC_ALL: \"%s\"\n", loc);
    return 0;
}

Il programma potrebbe emettere il risultato seguente:

LC_ALL: "it_IT.UTF-8"

581.3   Caratteri multibyte e caratteri estesi

All'origine del linguaggio C esisteva una corrispondenza biunivoca tra carattere e byte. Attualmente, questa corrispondenza riguarda solo i caratteri dell'insieme minimo, il quale di norma coincide con quello della codifica ASCII. Per rappresentare caratteri che vanno al di fuori dell'insieme minimo, si usano due metodi nel linguaggio: le sequenze multibyte, in cui un carattere è rappresentato attraverso una sequenza di più byte o comunque attraverso l'inserzione di codici che cambiano di volta in volta il sottoinsieme di riferimento, e i caratteri estesi che richiedono una unità di memorizzazione con un rango maggiore del byte. L'esempio seguente mostra l'uso di una stringa multibyte:

    printf ("€àèìòασδφ\n");

È il contesto che fa capire la natura della stringa. In pratica, il file sorgente che contiene i caratteri deve essere scritto utilizzando una qualche codifica che preveda l'uso di più byte per rappresentare un carattere. La stessa codifica è quella che il programma deve usare durante il funzionamento per interpretare correttamente la stringa multibyte fornita.

In questo caso particolare, la funzione printf() non ha nemmeno bisogno di rendersi conto della codifica; semplicemente, se il programma funziona secondo la configurazione corretta, la visualizzazione del messaggio avviene come previsto.

Esistono diversi modi di gestire delle sequenze multibyte per rappresentare caratteri particolari, ma alcune sono particolarmente difficili da amministrare, perché richiedono il passaggio a sottoinsiemi di caratteri differenti attraverso l'uso di codici speciali, a cui si fa riferimento con il termine shift. In pratica, in tali condizioni, quando deve essere interpretata una stringa contenente sequenze multibyte, le funzioni devono tenere traccia dello stato di questa interpretazione, per sapere a quale sottoinsieme particolare di caratteri si sta facendo riferimento. Pertanto, l'interruzione e la ripresa di tale interpretazione devono essere motivo di preoccupazione per il programmatore. Fortunatamente la tendenza è quella di usare la codifica UTF-8 per la rappresentazione dell'insieme universale dei caratteri, per tutte le lingue e tutte le nazionalità. Tale codifica ha il vantaggio di non richiedere la conservazione di uno stato (shift status), in quanto l'interpretazione di ogni carattere è indipendente dai precedenti: quello che è importante è evitare di spezzare l'interpretazione di un carattere a metà, ma anche se fosse, i caratteri successivi verrebbero individuati correttamente.

Dall'esempio mostrato si intende che una stringa multibyte si rappresenta letteralmente nello stesso modo di una stringa normale, con la differenza che la sua lunghezza in «caratteri», nel senso di unità char, è maggiore dei caratteri che rappresenta. quindi, eventualmente, nel dimensionare un array di caratteri, occorre tenere conto di questo particolare.

Per rappresentare un carattere che va al di fuori dell'insieme minimo del linguaggio C, si può usare un carattere esteso, ovvero un valore intero di rango maggiore rispetto al tipo char. Si tratta precisamente del tipo wchar_t (wide char) che in condizioni normali va dai 16 ai 32 bit;.

Evidentemente, il rango del tipo wchar_t condiziona la quantità di caratteri che possono essere rappresentati. Per una rappresentazione abbastanza completa dell'insieme universale serve almeno un tipo wchar_t da 32 bit.

Si può rappresentare una costante letterale di tipo wchar_t mettendo anteriormente il prefisso L. Per esempio, L'€' viene convertito dal compilatore in un carattere esteso che rappresenta numericamente il simbolo dell'euro. In modo analogo è possibile costruire array di elementi wchar_t, per contenere stringhe estese (stringhe di caratteri wchar_t concluse da un valore nullo di terminazione, come per le stringhe normali). Anche per rappresentare le stringhe estese in modo letterale si può usare il prefisso L. Per esempio, L"àèìòù" viene tradotto dal compilatore in una stringa estesa.

#include <stdio.h>
#include <locale.h>
int main (int argc, char *argv[])
{
    setlocale (LC_ALL, "en_US.UTF-8");
    wchar_t wc = L'ß';
    wchar_t wcs[] = L"€àèìòασδφ";
    printf ("%lc, %ls\n", wc, wcs);
    return 0;
}

L'esempio mostra l'uso delle costanti letterali riferite a caratteri e stringhe estese. In particolare, va osservato l'uso della funzione printf(), in cui si indicano lo specificatore di conversione %lc per tradurre un carattere esteso e %ls per una stringa estesa. Ecco il risultato che si attende di visualizzare da quel programma:

ß, €àèìòασδφ

A questo punto è bene sia chiaro un concetto logico ma non sempre evidente: per gestire caratteri al di fuori dell'insieme minimo, è necessario definire la configurazione locale con una codifica che sia tale da permetterlo. Pertanto, se non si usa la funzione setlocale() (così come invece avviene nell'esempio), si sta lavorando con la configurazione predefinita C, per la quale non ci sono sequenze multibyte e diventa inutile l'uso del tipo wchar_t. Pertanto, se nell'esempio mancasse l'uso appropriato della funzione setlocale(), non si otterrebbe la visualizzazione del testo come previsto.

581.4   Concatenamento eterogeneo

Il concatenamento di stringhe espresse in forma di costanti letterali, avviene, per le stringhe estese, esattamente come per le stringhe tradizionali, con l'eccezione che il concatenamento eterogeneo è ammissibile e implica sempre l'interpretazione di stringhe estese:

...
    wcp = "ciao amore" L"€àèìòασδφ";
...

In questo caso, la variabile wcp riceve il puntatore a una stringa estesa contenente precisamente la sequenza «ciao amore€àèìòασδφ», conclusa in modo appropriato.

Questo meccanismo consente, tra le altre cose, di concatenare delle macro-variabili che si espandono in stringhe letterali normali, in ogni circostanza, senza doverle duplicare per distinguerle in base al contesto.

581.5   Conversione tra caratteri multibyte e caratteri estesi

Un gruppo di funzioni dichiarate nel file stdlib.h è importante per gestire la conversione tra caratteri multibyte e caratteri estesi. Le funzioni più importanti sono precisamente mbstowcs() (Multibyte string to wide character string) e wcstombs() (Wide character string to multibyte string), con lo scopo di convertire stringhe da multibyte a caratteri estesi e viceversa.

size_t mbstowcs (wchar_t *restrict wcs, const char *restrict s, size_t n);
size_t wcstombs (char *restrict s, const wchar_t *restrict wcs, size_t n);

La funzione mbstowcs si usa per convertire una stringa contenente sequenze multibyte in una stringa estesa, ovvero un array di elementi wchar_t. L'ultimo parametro rappresenta la quantità massima di caratteri estesi che devono essere inseriti nella stringa estesa di destinazione, contando anche il carattere nullo di terminazione. Il valore restituito è la quantità di caratteri che sono stati inseriti, escludendo il carattere nullo di terminazione, se c'è.

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

int main (void)
{
    setlocale (LC_ALL, "en_US.UTF-8");
    wchar_t wca[] = L"€€€€€€€€€€";
    wchar_t wcb[] = L"€€€€€€€€€€";
    size_t q;

    q = mbstowcs (wca, "äåâ", 3);
    printf ("mbstowcs: %d: \"%ls\"\n", q, wca);

    q = mbstowcs (wcb, "äåâ", 6);
    printf ("mbstowcs: %d: \"%ls\"\n", q, wcb);

    return 0;
}

L'esempio mostra la dichiarazione di due stringhe estese contenenti 10 caratteri estesi (oltre al carattere di terminazione della stringa). La funzione mbstowcs() viene usata la prima volta per tradurre la stringa multibyte L"äåâ" nei caratteri estesi corrispondenti all'inizio della prima delle due stringhe estese. Però, viene posto il limite al trasferimento di soli tre caratteri. Così facendo, il carattere di terminazione della stringa multibyte non viene convertito. Nel secondo caso, invece, si richiede il trasferimento di sei caratteri estesi, ma questo si ferma quando viene incontrato il carattere nullo di terminazione.

Entrambe le chiamate alla funzione mbstowcs() restituiscono il valore tre, perché sono solo tre i caratteri trasferiti, che siano diversi da quello di terminazione, ma nel secondo caso si può apprezzare la differenza nella stringa estesa risultante:

mbstowcs: 3: "äå €€€€€"
mbstowcs: 3: "äåâ"

La funzione wcstombs() funziona in modo opposto, per convertire una stringa estesa in una stringa multibyte. In questo caso, l'ultimo parametro rappresenta la quantità di byte che si vogliono ottenere con il trasferimento, incluso quello che rappresenta la terminazione della stringa. Logicamente, come nel caso dell'altra funzione, si ottiene la quantità di byte ottenuti dal trasferimento, ma senza contare il carattere nullo di terminazione.

#include <locale.h>
#include <stdio.h>
#include <stdlib.h>

int main (void)
{
    setlocale (LC_ALL, "en_US.UTF-8");
    char mba[] = "**********";
    char mbb[] = "**********";
    size_t n;

    n = wcstombs (mba, L"äåâ", 6);
    printf ("wcstombs: %d: \"%s\"\n", n, mba);

    n = wcstombs (mbb, L"äåâ", 9);
    printf ("wcstombs: %d: \"%s\"\n", n, mbb);

    return 0;
}

Questo nuovo esempio è analogo al precedente, ma invertendo il ruolo delle stringhe: questa volta la stringa estesa viene convertita in una stringa multibyte. Nel caso particolare della codifica UTF-8, ognuna delle lettere che si vedono nella stringa estesa si traduce in una sequenza di due byte; pertanto, la conversione richiede che siano convertiti almeno sette byte, per includere anche il carattere nullo di terminazione. Si può vedere che nel primo caso il carattere nullo non viene convertito, pertanto la stringa di destinazione continua ad apparire della lunghezza originale, pur con la prima parte sovrascritta. Naturalmente, rimangono solo quattro asterischi perché la sequenza multibyte necessaria a rappresentare quelle tre lettere è complessivamente di sei byte.

wcstombs: 6: "äåâ****"
wcstombs: 6: "äåâ"

La conversione, in un verso o nell'altro, può fallire. Se queste funzioni incontrano dei problemi, restituiscono l'equivalente di -1 tradotto secondo il tipo size_t (in pratica, utilizzando una rappresentazione dei valori negativi in complemento a due, si ottiene il valore positivo massimo che la variabile possa rappresentare, essendo size_t senza segno).

Ci sono altri dettagli sull'uso di queste funzioni, ma si possono approfondire leggendo la sezione 597.11 e le pagine di manuale mbstowcs(3) wcstombs(3).


Appunti di informatica libera 2008 --- Copyright © 2000-2008 Daniele Giacomini -- <appunti2 (ad) gmail·com>


Dovrebbe essere possibile fare riferimento a questa pagina anche con il nome c_configurazione_locale.htm

[successivo] [precedente] [inizio] [fine] [indice generale] [indice ridotto] [indice analitico] [home]

Valid ISO-HTML!

CSS validator!

Gjlg Metamotore e Web Directory