13 Stimmen

Gibt es eine Möglichkeit, von UTF8 nach ISO-8859-1 zu konvertieren?

Meine Software erhält einige Zeichenfolgen in UTF8, die ich in ISO 8859 1 konvertieren muss. Ich weiß, dass der UTF8-Bereich größer ist als ISO 8859. Aber die Daten in UTF8 wurden zuvor von ISO hochkonvertiert, so dass ich nichts verpassen sollte.

Ich würde gerne wissen, ob es eine einfache / direkte Möglichkeit gibt, von UTF8 nach iso-8859-1 zu konvertieren.

16voto

Nominal Animal Punkte 36323

Hier ist eine Funktion, die Sie vielleicht nützlich finden: utf8_to_latin9() . Es konvertiert in ISO-8859-15 (einschließlich EURO, der ISO-8859-1 nicht hat), funktioniert aber auch korrekt für den UTF-8 -> ISO-8859-1 Umwandlung Teil einer ISO-8859-1 -> UTF-8 -> ISO-8859-1 Hin- und Rückfahrt.

Die Funktion ignoriert ungültige Codepunkte ähnlich wie //IGNORE Flag für iconv, setzt aber dekomponierte UTF-8-Sequenzen nicht neu zusammen, d. h., es wird nicht in U+006E U+0303 in U+00F1 . Ich mache mir nicht die Mühe, neu zu komponieren, weil iconv das auch nicht tut.

Die Funktion ist sehr vorsichtig, was den Zugriff auf Zeichenketten angeht. Sie scannt nie über den Puffer hinaus. Der Ausgabepuffer muss um ein Byte länger sein als die Länge, da sie immer das NUL-Byte am Ende der Zeichenkette anhängt. Die Funktion gibt die Anzahl der Zeichen (Bytes) in der Ausgabe zurück, ohne das NUL-Byte am Ende der Zeichenkette.

/* 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);
}

Beachten Sie, dass Sie eine benutzerdefinierte Transliteration für bestimmte Codepunkte in der Datei to_latin9() Funktion, aber Sie sind auf die Ersetzung eines Zeichens beschränkt.

So wie sie derzeit geschrieben ist, kann die Funktion eine sichere In-Place-Konvertierung durchführen: Eingabe- und Ausgabezeiger können identisch sein. Die Ausgabezeichenkette wird nie länger als die Eingabezeichenkette sein. Wenn Ihre Eingabezeichenkette Platz für ein zusätzliches Byte hat (z.B. durch das NUL am Ende der Zeichenkette), können Sie die obige Funktion sicher verwenden, um sie von UTF-8 nach ISO-8859-1/15 zu konvertieren. Ich habe es absichtlich so geschrieben, weil es Ihnen in einer eingebetteten Umgebung einige Mühe ersparen sollte, obwohl dieser Ansatz in Bezug auf die Anpassung und Erweiterung etwas eingeschränkt ist.

Edita:

Ich habe ein Paar Konvertierungsfunktionen hinzugefügt in einem Edit zu dieser Antwort für die Umwandlung von Latin-1/9 in/aus UTF-8 (ISO-8859-1 oder -15 in/aus UTF-8); der Hauptunterschied besteht darin, dass diese Funktionen eine dynamisch zugewiesene Kopie zurückgeben und die ursprüngliche Zeichenkette intakt lassen.

15voto

kay Punkte 24428

iconv - Zeichensatzkonvertierung durchführen

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 es "ISO_8859-1" y fromcode es "UTF-8" .

Arbeitsbeispiel:

#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;
}

0voto

Das folgende Beispiel verwendet ebenfalls die iconv-Bibliothek. Es funktioniert auch, wenn Sie eine Datei (oder einen Eingabestrom) haben, die gemischte UTF-8- und ISO-8859-1-Zeichen enthält (dies könnte zum Beispiel passieren, wenn Sie eine UTF-8-Datei haben und sie in einer Umgebung bearbeiten, die ISO-8859-1 verwendet).

int Utf8ToLatin1(char* input, char* output, size_t size)
{
    size_t in_left = size;
    size_t out_left = size;

    char *in_buf    = input;
    char *out_buf   = output;

    iconv_t cd = iconv_open("ISO_8859-1", "UTF-8");
    if (cd == (iconv_t)-1) {
        (void) fprintf(stderr, "iconv_open() failed, msg encoding will be kept!");
        strncpy(output, input, size);
        return -1;
    }

    do {
        if (iconv(cd, &in_buf, &in_left, &out_buf, &out_left) == (size_t) -1) {
            if (errno == EILSEQ) {
                /* Input conversion stopped due to an input byte that
                 * does not belong to the input codeset.
                 */
                printf("Input conversion stopped due to an input byte that does not belong to the input codeset.\n");
                *out_buf= *in_buf;
                out_buf++   ;out_left--;
                in_buf++    ;in_left--;

            } else if (errno == E2BIG) {
                /* Input conversion stopped due to lack of space in
                 * the output buffer.
                 */
                printf("Input conversion stopped due to lack of space in the output buffer.\n");
                perror("iconv failed!, propably the encoding is already Latin, msg encoding will be kept!\n");
                strncpy(output, input, size);
                return -1;
            } else if (errno == EINVAL) {
                /* Input conversion stopped due to an incomplete
                 * character or shift sequence at the end of the
                 * input buffer.
                 */
                printf("Input conversion stopped due to an incomplete character or shift sequence at the end of the input buffer.\n");
                *out_buf= *in_buf;
                out_buf++   ;out_left--;
                in_buf++    ;in_left--;
            }

        }
    } while (in_left > 0 && out_left > 0);
    *out_buf = 0;

    iconv_close(cd);

    printf("*********************************************************\n");
    printf("ISO-8859-1:\n %s\n", input, output);
    return 0;

}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X