Unicode UTF Converter


17

Ziel ist es, einen vollständig kompatiblen Konverter zwischen den offiziellen Unicode-Codierungen zu erstellen, wie in den UTF-FAQ angegeben . Da dies auf Unicode zentriert ist, akzeptiere ich die Antwort mit der niedrigsten Byteanzahl unter Verwendung der bestmöglichen der beteiligten Codierungen (die wahrscheinlich UTF-8 sein wird, es sei denn, Sie programmieren sie in APL). Ich entschuldige mich für den langen Beitrag, aber ein Großteil davon erklärt die Kodierungen, auf die auch in der offiziellen Spezifikation (pdf, Abschnitt 3.9 D90 - D92) oder in Wikipedia zugegriffen werden kann .

Spezifikationen

Wenn Ihre gewählte Sprache zu irgendeinem Zeitpunkt eine Anforderung nicht genau erfüllen kann, ersetzen Sie sie durch etwas, das dem Geist der angegebenen Regeln entspricht. Z.B. Nicht jede Sprache verfügt über eingebaute Arrays, Funktionen usw.

  • Verwenden Sie keine Zeichenfolgenbibliotheken / -funktionen oder Kodierungsbibliotheken / -funktionen. Der Sinn dieses Codegolfs besteht darin, den Konverter unter Verwendung von Bit / Byte-Manipulation zu implementieren. Die Verwendung von Strings selbst als Zeichen- oder Byte-Array ist jedoch zulässig. Oh, und auch keine OS-Aufrufe, die die Konvertierung durchführen.

  • Der Konverter ist eine Funktion, die drei Parameter akzeptiert: ein Byte-Array, das die codierte Eingabezeichenfolge darstellt, und die als Zahlen dargestellten "Eingabe" - und "Ausgabe" -Codierungen. Beliebig vergeben wir UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, and UTF32LENummern von 0 bis 6 in dieser Reihenfolge. Es muss nicht überprüft werden, ob die Zahl < 0oder > 6ist. Wir gehen davon aus, dass diese Parameter korrekt sind. Der Konverter gibt ein gültiges Byte-Array in der gewünschten Ausgabecodierung zurück.

  • Wir werden das Nullzeichen ( U+0000) als Zeichenkettenabschluss verwenden. Alles was danach passiert ist egal. Wir gehen davon aus, dass das Eingabearray irgendwo das Null-Zeichen hat, sodass Sie keine Begrenzungsprüfung durchführen müssen.

  • Laut FAQ müssen wir einen Fehler melden , wenn das Eingabebyte-Array für seine deklarierte Codierung ungültig ist. Wir werden dies auf eine der folgenden Arten tun: Absturz des Programms, Auslösen einer Ausnahme, Zurückgeben von Null oder Zurückgeben eines Arrays, dessen erste vier Bytes alle 0 sind (damit es wie U+0000in jeder Codierung erkannt werden kann ).

Die Kodierungen

Die offiziellen Spezifikationen müssen befolgt werden, aber Wikipedia bietet eine gute (und meines Erachtens korrekte) Erklärung der Kodierungen, und der Vollständigkeit halber werde ich sie hier zusammenfassen. Beachten Sie, dass UTF-16 und UTF-32 Varianten für Endianness haben .

UTF-32, UTF-32LE, UTF-32BE

Bei der einfachsten Codierung wird jeder Codepunkt einfach in 4 Bytes entsprechend seinem numerischen Wert codiert. LE / BE steht für Endianness (Little Endian / Big Endian).

UTF-16, UTF-16LE, UTF-16BE

Codepunkte von U+0000 - U+FFFFwerden in 2 Bytes entsprechend ihrem numerischen Wert codiert. Größere Werte werden mit einem Paar Ersatzzeichen codiert, die reservierte Werte von sind U+D800 - U+DFFF. Um Punkte zu kodieren U+FFFF, die größer als sind , kann der folgende Algorithmus verwendet werden (unverschämt aus Wikipedia kopiert ):

  • 0x010000 wird vom Codepunkt abgezogen, wobei eine 20-Bit-Zahl im Bereich von 0..0x0FFFFF verbleibt.
  • Die oberen zehn Bits (eine Zahl im Bereich 0..0x03FF) werden zu 0xD800 hinzugefügt, um die erste Codeeinheit oder den ersten Ersatz zu erhalten, der im Bereich 0xD800..0xDBFF [...] liegt.
  • Die unteren zehn Bits (ebenfalls im Bereich 0..0x03FF) werden zu 0xDC00 addiert, um die zweite Codeeinheit oder den zweiten Ersatz zu ergeben, der im Bereich 0xDC00..0xDFFF [...] liegt.

UTF-8

Codepunkte von U+0000 - U+007Fwerden als 1 Byte entsprechend ihrem numerischen Wert codiert. Von U+0080 - U+07FFsie als codiert sind 110xxxxx 10xxxxxx, U+0800 - U+FFFFist 1110xxxx 10xxxxxx 10xxxxxx, sind höhere Werte 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. Die x‚s sind die Bits aus dem numerischen Wert des Codepunktes.

Stückliste

Die Bytereihenfolge-Markierung (BOM U+FEFF) wird als erster Codepunkt verwendet, um die Endianität anzuzeigen. Gemäß den FAQ-Richtlinien zu Stücklisten wird die Stückliste wie folgt verwendet: UTF-8, UTF-16 and UTF-32Sie ist optional. Fehlt die Stückliste in UTF-16oder UTF-32, wird von einem Big Endian ausgegangen. Die Stückliste darf nicht in erscheinen UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE.

Häufige Fehler, die zu ungültigem UTF führen

Verschiedene Dinge können dazu führen, dass eine Byte-Sequenz ungültiges UTF ist.

  • UTF-8 und UTF-32: Direktes Codieren von Ersatzcodepunkten ( U+D800 - U+DFFF) oder Codepunkten größer als U+10FFFF.
  • UTF-8: Viele ungültige Bytefolgen.
  • UTF-16: Nicht gepaarte oder falsch gepaarte Ersatzzeichen.
  • Stückliste: Muss wie im Codierungsabschnitt angegeben verwendet werden. Beachten Sie, dass Sie bei der Ausgabe UTF-16oder UTF-32(ohne Angabe einer inhärenten Endianzahl) auswählen können, bei Little Endian jedoch die Stückliste einbeziehen müssen.

Beachten Sie, dass Nicht-Zeichen und nicht zugewiesene Codepunkte (beide von Surrogaten verschieden) wie normale Zeichen behandelt werden.


Msgstr "Keine Verwendung von String - Bibliotheken / Funktionen oder Kodierung von Bibliotheken / Funktionen." Was ist mit echten Einbauten? In APL ''⎕R''⍠'InEnc' 'UTF16BE' 'OutEnc' 'UTF8-BOM'.
Adám

2
@NBZ Diese sind nicht zulässig, da der Sinn dieser Herausforderung darin besteht, das von ihnen bereitgestellte Verhalten zu implementieren.
DPenner1

Hinweis für die Beantworter: Ich habe diese Frage mehr oder weniger aufgegeben, aber angesichts des jüngsten erneuten Interesses werde ich mir in den kommenden Tagen einige Zeit nehmen, um die Antworten durchzugehen.
DPenner1

Antworten:


3

C ++, (UTF-8) 971 Bytes

#include<cstdint>
using u=uint8_t;using U=uint32_t;U i,o,x,b,m;U R(u*&p){x=*p++;if(!i){m=0;while(128>>m&x)++m;if(m>1)for(x&=127>>m;--m;)x=x<<6|((*p&192)-128?~0:*p++&63);return m?x=~0:x;}else if(i<3){x<<=8;x+=*p++;}else if(i<4){x+=*p++<<8;}else if(i<6){x<<=24;x+=*p++<<16;x+=*p++<<8;x+=*p++;}else{x+=*p++<<8;x+=*p++<<16;x+=*p++<<24;}return x;}U r(u*&p){U x0=R(p);if(i&&i<4&&x>>10==54)x=R(p)>>10==55?(x0<<10)+x-56613888:~0;if(!b++){if(x==65279)if(!i||i%3==1)r(p);else x=~0;else if(x==65534&&i==1)i=3,r(p);else if(x==4294836224&&i==4)i=6,r(p);}return x>1114111||x>>11==27?x=~0:x;}void w(U x,u*&p){if(!o){if(x<128)*p++=x;else{for(m=0;~63<<m&x;m+=6);for(*p++=~127>>m/6|x>>m;m;)*p++=128|x>>(m-=6)&63;}}else if(o<4&&x>65535)x-=65536,w(55296|x>>10,p),w(56320|x&1023,p);else if(o<3)*p++=x>>8,*p++=x;else if(o<4)*p++=x,*p++=x>>8;else if(o<6)*p++=x>>24,*p++=x>>16,*p++=x>>8,*p++=x;else*p++=x,*p++=x>>8,*p++=x>>16,*p++=x>>24;}int t(u*&p,u*&q){for(b=0,x=1;U(x+x);)w(r(p),q);return x;}

Das unten stehende lesbare Programm kann durch Filtern mit dem folgenden Perl-Befehl auf die obige Form komprimiert werden:

perl -p0 -e 's!//.*!!g;s/\s+/ /g;s/ \B|\B //g;s/0x[\da-f]+/hex($&)/ige;s/#include<[^<>]+>/\n$&\n/g;s/^\n+//mg'

Der obige Befehl

  • entfernt Kommentare
  • Entfernt unnötige Leerzeichen
  • konvertiert hexadezimale Literale in Dezimalzahlen
  • Wiedereinsetzung von Zeilenumbrüchen um #includeLinien

Lesbarer Code

#include <cstdint>
using u = uint8_t;
using U = uint32_t;

U   i,                          // input encoding
    o,                          // output encoding
    x,                          // last read value
    b,                          // char count(BOM only valid when b==0)
    m;                          // temporary variable for measuring UTF-8

//   Encodings:
// 0 UTF-8
// 1 UTF-16
// 2 UTF-16BE
// 3 UTF-16LE
// 4 UTF-32
// 5 UTF-32BE
// 6 UTF-32LE

// Read a character or UTF-16 surrogate
U R(u*& p) {
    x = *p++;
    if (!i) { // UTF-8
        m=0; while (128>>m&x) ++m; // how many bytes?
        if (m>1) for (x&=127>>m; --m; ) x = x<<6 | ((*p&192)-128?~0:*p++&63);
        return m ? x=~0 : x;
    } else if (i<3) { // UTF-16, UTF-16BE
        x<<=8; x+=*p++;
    } else if (i<4) { // UTF-16LE
        x+=*p++<<8;
    } else if (i<6) { // UTF-32, UTF-32BE
        x<<=24; x+=*p++<<16; x+=*p++<<8; x+=*p++;
    } else { // UTF-32LE
        x+=*p++<<8; x+=*p++<<16; x+=*p++<<24;
    }
    return x;
}

// Read a character, combining surrogates, processing BOM, and checking range
U r(u*& p) {
    U x0 = R(p);
    if (i && i<4 && x>>10==54)
        x = R(p)>>10==55 ? (x0<<10)+x-56613888: ~0; // 56613888 == 0xd800<<10 + 0xdc00 - 0x10000
    if (!b++) {                 // first char - is it BOM?
        if (x==0xFEFF)
            if (!i || i%3==1)
                r(p); // BOM in UTF-8 or UTF-16 or UTF-32 - ignore, and read next char
            else
                x = ~0; // not allowed in these modes
        else if (x==0xFFFE && i==1)
            i=3,r(p); // reversed BOM in UTF-16 - change to little-endian, and read next char
        else if (x==0xFFFE0000 && i==4)
            i=6,r(p); // reversed BOM in UTF-32 - change to little-endian, and read next char
    }
    return x>0x10ffff || x>>11==27 ? x=~0 : x;
}


// Write character(assumed in-range)
void w(U x, u*& p) {
    if (!o) { // UTF-8
        if (x<128) *p++=x;        // ASCII
        else {
            for (m=0; ~63<<m&x; m+=6); // how many bits?
            for (*p++=~127>>m/6|x>>m; m; ) *p++ = 128|x>>(m-=6)&63;
        }
    } else if (o<4 && x>65535)  // UTF-16 surrogate
        x-=65536, w(0xD800|x>>10,p), w(0xDC00|x&0x3FF,p);
    else if (o<3)  // UTF-16, UTF-16BE
        *p++=x>>8, *p++=x;
    else if (o<4)  // UTF-16LE
        *p++=x, *p++=x>>8;
    else if (o<6)  // UTF-32, UTF-32BE
        *p++=x>>24, *p++=x>>16, *p++=x>>8, *p++=x;
    else  // UTF-32LE
        *p++=x, *p++=x>>8, *p++=x>>16, *p++=x>>24;
}

// Transcode
int t(u*& p, u*& q)                  // input, output
{
    for (b=0,x=1;U(x+x);)    // exit condition is true only for x==-x, i.e. 0 and ~0
        w(r(p),q);
    return x;
}

Die Funktion wird aufgerufen ist t(), mit Ein- und Ausgang Kodierungen in den globalen Variablen übergeben iund obzw. und pzeigte auf dem Bytes des Eingangs, der muss nullterminierte sein. qzeigt auf den Ausgabepuffer, der überschrieben wird und für das Ergebnis groß genug sein muss - es wird nicht versucht, einen Pufferüberlauf zu vermeiden.

Ich hoffe, die Code-Kommentare sind ausreichend erklärend - fragen Sie unten, ob einer von ihnen zu kryptisch ist (aber versuchen Sie es zuerst!).

Ich habe eine umfangreiche Testsuite zusammengestellt, während ich diese Antwort entwickelte. Ich füge es unten zum Nutzen anderer Teilnehmer ein und dokumentiere meine Interpretation der Anforderungen:

Testfunktionen

#include <vector>
#include <iostream>

std::ostream& operator<<(std::ostream& out, const std::vector<u>& v)
{
    out << "{ ";
    for (int i: v) out << i << " ";
    out << "}";
    return out;
}

int test_read(int encoding, std::vector<u> input, U expected)
{
    b = 0;
    i = encoding;
    auto d = input.data();
    U actual = r(d);
    if (actual == expected) return 0;
    std::cerr << std::hex << "Decoding " << encoding << "; " << input << " gave " << actual
              << " instead of " << expected << std::endl;
    return 1;
}

int test_write(int encoding, U input, std::vector<u> expected)
{
    o = encoding;
    u buf[20], *p = buf;
    w(input, p);
    std::vector<u> actual(buf,p);
    if (expected == actual) return 0;
    std::cerr << std::hex << "Encoding " << encoding << "; " << input << " gave " << actual
              << " instead of " << expected << std::endl;
    return 1;
}

int test_transcode(int ienc, std::vector<u> input, int oenc, std::vector<u> expected)
{
    b = 0;
    i = ienc; o = oenc;
    u buf[200], *p = buf, *d = input.data();
    int result = t(d, p);
    std::vector<u> actual(buf,p);
    if (result ? expected.empty() : expected == actual) return 0;
    std::cerr << std::hex << "Encoding " << ienc << " to " << oenc << "; " << input << " gave " << actual
              << " instead of " << expected << std::endl;
    return 1;
}

Testsuite

static const U FAIL = ~0;
int main() {
    int e = 0;                        // error count
    // UTF-8
    e += test_read(0, { 128 }, FAIL); // unexpected continuation
    e += test_read(0, { 128, 1 }, FAIL);
    e += test_read(0, { 128, 128 }, FAIL);
    e += test_read(0, { 192, 192 }, FAIL); // start without continuation
    e += test_read(0, { 192, 0 }, FAIL);
    e += test_read(0, { 224, 0 }, FAIL);
    e += test_read(0, { 224, 192 }, FAIL);
    e += test_read(0, { 0xf4, 0x90, 128, 128 }, FAIL); // Unicode maximum+1

    e += test_read(0, { 127 }, 127);
    e += test_read(0, { 192, 129 }, 1); // We accept overlong UTF-8
    e += test_read(0, { 0xc2, 128 }, 128);
    e += test_read(0, { 224, 128, 129 }, 1);
    e += test_read(0, { 0xef, 128, 128 }, 0xF000);
    e += test_read(0, { 0xef, 191, 191 }, 0xFFFF);
    e += test_read(0, { 0xf4, 128, 128, 128 }, 0x100000);
    e += test_read(0, { 0xf4, 0x8f, 191, 191 }, 0x10FFFF); // Unicode maximum

    e += test_read(0, { 0xEF, 0xBB, 0xBF, 127 }, 127); // byte-order mark

    e += test_write(0, 0, { 0 });
    e += test_write(0, 127, { 127 });
    e += test_write(0, 128, { 0xc2, 128 });
    e += test_write(0, 255, { 0xc3, 191 });
    e += test_write(0, 0xFFFF, { 0xef, 191, 191 });
    e += test_write(0, 0x10FFFF, { 0xf4, 0x8f, 191, 191 });

    // UTF-16
    e += test_read(1, { 0, 1 }, 1);
    e += test_read(1, { 0xd8, 0, 0xdc, 1 }, 0x10001);
    e += test_read(1, { 0xdb, 0xff, 0xdf, 0xff }, 0x10ffff);

    e += test_read(1, { 0xd8, 0, 0xd8, 1 }, FAIL); // mismatched surrogate
    e += test_read(1, { 0xd8, 0, 0, 1 }, FAIL); // mismatched surrogate
    e += test_read(1, { 0xdc, 0 }, FAIL);

    e += test_write(1, 1, { 0, 1 });
    e += test_write(1, 256, { 1, 0 });
    e += test_write(1, 0xffff, { 255, 255 });
    e += test_write(1, 0x10001, { 0xd8, 0, 0xdc, 1 });
    e += test_write(1, 0x10ffff, { 0xdb, 0xff, 0xdf, 0xff });

    // UTF-16LE
    e += test_write(3, 1, { 1, 0 });
    e += test_write(3, 256, { 0, 1 });
    e += test_write(3, 0x10001, { 0, 0xd8, 1, 0xdc });
    e += test_write(3, 0x10fffe, { 0xff, 0xdb, 0xfe, 0xdf });

    // UTF-16 byte-order mark
    e += test_read(1, { 0xFE, 0xFF, 0x0, 1 }, 1); // byte-order mark
    e += test_read(1, { 0xFF, 0xFE, 1, 0x0 }, 1); // reversed byte-order mark
    // disallowed byte-order marks
    e += test_read(2, { 0xFE, 0xFF }, FAIL);
    e += test_read(3, { 0xFF, 0xFE }, FAIL);
    // reversed byte-order mark is an unassigned character - to be treated like regular character, according to question
    e += test_read(2, { 0xFF, 0xFE }, 0xfffe);
    e += test_read(3, { 0xFE, 0xFF }, 0xfffe);

    // UTF-32
    e += test_read(4, { 0, 0, 0, 1 }, 1);
    e += test_read(4, { 1, 0, 0, 0 }, FAIL);
    e += test_write(4, 1, { 0, 0, 0, 1 });
    e += test_write(4, 0x10203, { 0, 1, 2, 3 });

    // UTF-32LE
    e += test_read(6, { 0, 0, 0, 1 }, FAIL);
    e += test_read(6, { 1, 0, 0, 0 }, 1);

    // UTF-32 byte-order mark
    e += test_read(4, { 0, 0, 0xFE, 0xFF,  0, 0, 0, 1 }, 1); // byte-order mark
    e += test_read(4, { 0xFF, 0xFE, 0, 0,  1, 0, 0, 0 }, 1); // reversed byte-order mark
    // disallowed byte-order marks
    e += test_read(5, { 0, 0, 0xFE, 0xFF }, FAIL);
    e += test_read(5, { 0xFF, 0xFE, 0, 0 }, FAIL);
    e += test_read(6, { 0, 0, 0xFE, 0xFF }, FAIL);
    e += test_read(6, { 0xFF, 0xFE, 0, 0 }, FAIL);

    e += test_transcode(1, { 1, 2, 0xFE, 0xFF, 0, 0 }, // That's not a BOM; it's a zwnj when not the first char
                        1, { 1, 2, 0xFE, 0xFF, 0, 0 });
    e += test_transcode(1, { 0xFF, 0xFE, 1, 2, 0, 0 }, // reversed byte-order mark implies little-endian
                        1, { 2, 1, 0, 0 });
    e += test_transcode(4, { 0xFF, 0xFE, 0, 0, 1, 2, 0, 0, 0, 0 }, // reversed BOM means little-endian
                        4, { 0, 0, 2, 1, 0, 0, 0, 0 });
    e += test_transcode(1, { 0xdb, 0xff, 0xdf, 0xff, 0, 0 }, // U+10ffff UTF-16 to UTF-8
                        0, { 0xf4, 0x8f, 191, 191, 0 });

    return e;
}

Dang .. C ++ schlug Python.
TickTock

5

Python - 1367 UTF-8-Zeichen

In Ordung! Dies war eine äußerst schwierige Frage, da das Verstehen und Implementieren aller Spezifikationen viel Arbeit gekostet hat, aber ich denke, dass ich eine korrekte Implementierung habe.

O,P,Q,R=65536,128,b'\xff\xfe\x00\x00',63
def A(x,y):assert x;return y
def B(x):
    o,c=[],0
    for b in x:
        if c:c,v=c-1,A(127<b<192,v<<6)|(b-P)
        else:
            c,v=(b>127)+(b>223)+(b>239),b
            if b>127:v=A(191<b<248,b&(R>>c))
        o+=[v][c:]
    return o[o[0]in(65279,O-2):]
def C(k):
    def o(x,s=None):
        for a,b in zip(x[k::2],x[1-k::2]):
            d=a|(b<<8)
            if s!=None:yield(A(56319<d<57344,d-56320)|(s<<10))+O;s=None
            elif 55295<d<57344:s=A(s<1024,d-55296)
            else:yield d
    return o
def D(x):n=(2,3,1)[[Q[:2],Q[1::-1],x[:2]].index(x[:2])];return C(n&1)(x[n&2:])
E=lambda a,b,c,d:lambda x:[L|(l<<8)|(m<<16) for L,l,m in zip(x[a::4],x[b::4],x[c::4])]
def F(x):n,m=((1,4),(-1,4),(-1,0))[[Q,Q[::-1],x[:4]].index(x[:4])];return E(*range(4)[::n])(x[m:])
S=lambda x,s=0,a=255:(x>>s)&a
G=lambda e:(e,)if e<P else(192|S(e,6),P|(e&R))if e<2048 else(224|S(e,12),P|S(e,6,R),P|(e&R))if e<O else(240|S(e,18),P|S(e,12,R),P|S(e,6,R),P|(e&R))
H=lambda e:(S(e,8),S(e))if e<O else(216|S(e-O,18),S(e-O,10),220+S((e-O)&1023,8),S(e-O))
I=lambda e:(S(e),S(e,8))if e<O else(S(e-O,10),216|S(e-O,18),S(e-O),220+S((e-O)&1023,8))
J=lambda e:(S(e,24),S(e,16),S(e,8),S(e))
K=lambda e:(S(e),S(e,8),S(e,16),S(e,24))
convert=lambda d,i,o:bytes(sum(map(L[o],N(list(M[i](d)))),()))if d else d
L,M=[G,H,H,I,J,J,K],[B,D,C(1),C(0),F,E(3,2,1,0),E(0,1,2,3)]
N=lambda d:[A(-1<x<1114112 and x&~2047!=55296,x)for x in d]

convertist die Funktion, die das Datenobjekt "Bytes", die Eingabe-ID und die Ausgabe-ID verwendet. Es scheint zu funktionieren - obwohl in Python die Verwendung von Stücklisten anscheinend leicht fehlerhaft ist, wenn dies in der Codierung nicht angegeben ist, funktioniert es nicht, die in Python integrierte Codierung zum Testen der Modi 1 und 4 zu verwenden.

Fun Tatsache: Die Größe ist auch 555 16 oder 10101010101 2 .

773 Zeichen für die Dekodierung, 452 für die Kodierung, 59 für die Verifizierung und 83 für verschiedene Teile.


@TrangOul: Im Allgemeinen sind einfache Bearbeitungen (wie das Markieren von Sprachen) verpönt.
Zach Gates


Diese Frage / Antwort zeigt keinen Community-Konsens. Im gesamten Netzwerk sind solche geringfügigen Änderungen verpönt. Weder <1000 noch> 1000 Mitarbeiter sollten diese Änderungen vornehmen, es sei denn, sie verbessern den Inhalt oder das Format eindeutig. Es ist am besten, die Bearbeitung von Dingen wie Sprachkennungen, Einzelwortkorrekturen / -änderungen usw. zu unterbrechen. @Cat
Zach Gates

Ich denke, die Größe ist nicht mehr 0x555 :-(. Aber Sie könnten näher kommen mit dem Standard-Python-Golf-Tipp, ein Leerzeichen für Einzüge zu verwenden.
Toby Speight

@TobySpeight es ist gerade 0x557, du hast recht. Tatsächlich habe ich Tabulatoren verwendet, die zum Posten in Leerzeichen umgewandelt werden mussten, aber immer noch als ein Zeichen zählen. Ich werde nachsehen, ob ich ein paar Charaktere auf andere Weise rasieren kann, wenn ich die Gelegenheit dazu bekomme.
Cel Skeggs

4

Python 3, 1138 Bytes (UTF-8)

Es stellt sich also heraus, dass 14 Stunden internationale Reise eine fantastische Gelegenheit sind, eine Golfherausforderung abzuschließen ...

Die Konvertierungsfunktion ist C(). Diese Anrufe u(), v()und w()zu dekodieren, und U(), V()und W()zu kodieren, UTF-8, -16 und -32 ist. Keiner der Encoder gibt eine Stückliste aus, aber alle Decoder verarbeiten eine Stückliste korrekt. Fehlerzustände führen zu einer Ausnahme (normalerweise ZeroDivisionErrormit freundlicher Genehmigung der Funktion "Plötzlich sterben" E()).

from struct import*
l=len
j=''.join
b=lambda c:[*bin(c)[2:]]
P,Q,i,o,z,Z='HI10><'
B=65279
O,F,H,L,X=1024,65536,55296,56320,57344
E=lambda:1/0
R=lambda y,e,c,n:unpack(([[z,Z][y[:n]==pack(Z+c,B)],e][l(e)])+c*(l(y)//n),y)
S=lambda d,e:B!=d[0]and d or e and E()or d[1:]
def u(y,d=(),p=0):
 while p<l(y):
  q=b(y[p])
  if l(q)>7:
   x=q.index(o);C=1<x<5and q[x+1:]or E();X=x+p;X>l(y)>E();p+=1
   while p<X:q=b(y[p]);C=l(q)>7and(i,o==q[:2])and(*C,*q[2:])or E();p+=1
   d=*d,int(j(C),2)
  else:d=*d,y[p];p+=1
 return S(d,0)
def T(p):
 q=b(p);C=()
 while l(q)not in(7,11,16,21):q=o,*q
 while l(q)>6:C=int(i+o+j(q[-6:]),2),*C;q=q[:-6]
 return bytes(p<128and[p]or[int(i*(7-l(q))+o+j(q),2),*C])
U=lambda c:b''.join(map(T,c))
def v(y,e=''):
 c=R(y,e,P,2);d=[];n=0
 while n<l(c)-1:h,a=c[n:n+2];D=[(h,a),(F+(h-H)*O+a-L,)][H<=h<L<=a<X];M=3-l(D);n+=M;d+=D[:M]
 if n<l(c):d=*d,c[n]
 return S(d,e)
V=lambda c,e=z:W(sum(map(lambda p:([H+(p-F)//O,L+(p-F)%O],[p])[p<F],c),[]),e,P)
w=lambda y,e='':S(R(y,e,Q,4),e)
W=lambda c,e=z,C=Q:pack(e+C*l(c),*c)
K=(u,U),(v,V),(v,V,z),(v,V,Z),(w,W),(w,W,z),(w,W,Z)
def C(y,f,t):f,_,*a=K[f];_,t,*b=K[t];return t(f(y,*a),*b)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.