GNU/Linux >> Linux Esercitazione >  >> Linux

C'è un modo per convertire da UTF8 a ISO-8859-1?

Ecco una funzione che potresti trovare utile:utf8_to_latin9() . Converte in ISO-8859-15 (incluso EURO, che ISO-8859-1 non ha), ma funziona correttamente anche per UTF-8 ->ISO-8859-1 conversione parte di un ISO-8859-1 ->UTF-8 ->ISO-8859-1 andata e ritorno.

La funzione ignora i punti di codice non validi simili a //IGNORE flag per iconv, ma non ricompone le sequenze UTF-8 scomposte; cioè, non diventerà U+006E U+0303 in U+00F1 . Non mi preoccupo di ricomporre perché nemmeno iconv lo fa.

La funzione è molto attenta all'accesso alle stringhe. Non eseguirà mai la scansione oltre il buffer. Il buffer di output deve essere un byte più lungo di length, poiché aggiunge sempre il byte NUL di fine stringa. La funzione restituisce il numero di caratteri (byte) in output, escluso il byte NUL di fine stringa.

/* UTF-8 to ISO-8859-1/ISO-8859-15 mapper.
 * Return 0..255 for valid ISO-8859-15 code points, 256 otherwise.
*/
static inline unsigned int to_latin9(const unsigned int code)
{
    /* Code points 0 to U+00FF are the same in both. */
    if (code < 256U)
        return code;
    switch (code) {
    case 0x0152U: return 188U; /* U+0152 = 0xBC: OE ligature */
    case 0x0153U: return 189U; /* U+0153 = 0xBD: oe ligature */
    case 0x0160U: return 166U; /* U+0160 = 0xA6: S with caron */
    case 0x0161U: return 168U; /* U+0161 = 0xA8: s with caron */
    case 0x0178U: return 190U; /* U+0178 = 0xBE: Y with diaresis */
    case 0x017DU: return 180U; /* U+017D = 0xB4: Z with caron */
    case 0x017EU: return 184U; /* U+017E = 0xB8: z with caron */
    case 0x20ACU: return 164U; /* U+20AC = 0xA4: Euro */
    default:      return 256U;
    }
}

/* Convert an UTF-8 string to ISO-8859-15.
 * All invalid sequences are ignored.
 * Note: output == input is allowed,
 * but   input < output < input + length
 * is not.
 * Output has to have room for (length+1) chars, including the trailing NUL byte.
*/
size_t utf8_to_latin9(char *const output, const char *const input, const size_t length)
{
    unsigned char             *out = (unsigned char *)output;
    const unsigned char       *in  = (const unsigned char *)input;
    const unsigned char *const end = (const unsigned char *)input + length;
    unsigned int               c;

    while (in < end)
        if (*in < 128)
            *(out++) = *(in++); /* Valid codepoint */
        else
        if (*in < 192)
            in++;               /* 10000000 .. 10111111 are invalid */
        else
        if (*in < 224) {        /* 110xxxxx 10xxxxxx */
            if (in + 1 >= end)
                break;
            if ((in[1] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x1FU)) << 6U)
                             |  ((unsigned int)(in[1] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 2;

        } else
        if (*in < 240) {        /* 1110xxxx 10xxxxxx 10xxxxxx */
            if (in + 2 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x0FU)) << 12U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[2] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 3;

        } else
        if (*in < 248) {        /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */
            if (in + 3 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x07U)) << 18U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[3] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 4;

        } else
        if (*in < 252) {        /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
            if (in + 4 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U &&
                (in[4] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x03U)) << 24U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 18U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[3] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[4] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 5;

        } else
        if (*in < 254) {        /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */
            if (in + 5 >= end)
                break;
            if ((in[1] & 192U) == 128U &&
                (in[2] & 192U) == 128U &&
                (in[3] & 192U) == 128U &&
                (in[4] & 192U) == 128U &&
                (in[5] & 192U) == 128U) {
                c = to_latin9( (((unsigned int)(in[0] & 0x01U)) << 30U)
                             | (((unsigned int)(in[1] & 0x3FU)) << 24U)
                             | (((unsigned int)(in[2] & 0x3FU)) << 18U)
                             | (((unsigned int)(in[3] & 0x3FU)) << 12U)
                             | (((unsigned int)(in[4] & 0x3FU)) << 6U)
                             |  ((unsigned int)(in[5] & 0x3FU)) );
                if (c < 256)
                    *(out++) = c;
            }
            in += 6;

        } else
            in++;               /* 11111110 and 11111111 are invalid */

    /* Terminate the output string. */
    *out = '\0';

    return (size_t)(out - (unsigned char *)output);
}

Tieni presente che puoi aggiungere una traslitterazione personalizzata per punti di codice specifici nel to_latin9() funzione, ma sei limitato alle sostituzioni di un carattere.

Come è attualmente scritto, la funzione può eseguire la conversione sul posto in modo sicuro:i puntatori di input e output possono essere gli stessi. La stringa di output non sarà mai più lunga della stringa di input. Se la tua stringa di input ha spazio per un byte extra (ad esempio, ha il NUL che termina la stringa), puoi tranquillamente utilizzare la funzione sopra per convertirla da UTF-8 a ISO-8859-1/15. L'ho scritto deliberatamente in questo modo, perché dovrebbe farti risparmiare qualche sforzo in un ambiente embedded, sebbene questo approccio sia un po' limitato rispetto a. personalizzazione ed estensione.

Modifica:

Ho incluso un paio di funzioni di conversione in una modifica a questa risposta sia per la conversione Latin-1/9 a/da UTF-8 (ISO-8859-1 o -15 a/da UTF-8); la differenza principale è che queste funzioni restituiscono una copia allocata dinamicamente e mantengono intatta la stringa originale.


iconv - esegue la conversione del set di caratteri

size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);

iconv_t iconv_open(const char *tocode, const char *fromcode);

tocode è "ISO_8859-1" e fromcode è "UTF-8" .

Esempio funzionante:

#include <iconv.h>
#include <stdio.h>

int main (void) {
    iconv_t cd = iconv_open("ISO_8859-1", "UTF-8");
    if (cd == (iconv_t) -1) {
        perror("iconv_open failed!");
        return 1;
    }

    char input[] = "Test äöü";
    char *in_buf = &input[0];
    size_t in_left = sizeof(input) - 1;

    char output[32];
    char *out_buf = &output[0];
    size_t out_left = sizeof(output) - 1;

    do {
        if (iconv(cd, &in_buf, &in_left, &out_buf, &out_left) == (size_t) -1) {
            perror("iconv failed!");
            return 1;
        }
    } while (in_left > 0 && out_left > 0);
    *out_buf = 0;

    iconv_close(cd);

    printf("%s -> %s\n", input, output);
    return 0;
}

Linux
  1. C'è un modo per ispezionare l'attuale rpath su Linux?

  2. Come convertire ISO8859-15 in UTF8?

  3. C'è un modo per riprendere uno scp interrotto di un file?

  4. C'è un modo per riavviare i badblock?

  5. C'è un modo per abilitare minicom linewrap per impostazione predefinita

Convertire il filesystem di avvio da Xfs a Ext4?

C'è un modo per "uniq" per colonna?

C'è un modo per ritagliare un file NETCDF?

C'è un modo per mostrare la notifica dallo script bash in Ubuntu?

C'è un modo per ascoltare il processo?

C'è un modo per ottenere la versione del BIOS da Linux?