Unicode UTF Converter

17

Tujuannya adalah untuk membuat konverter yang sepenuhnya sesuai antara pengkodean Unicode resmi seperti yang diberikan dalam FAQ UTF . Mengingat bahwa ini berpusat pada Unicode, saya akan menerima jawaban dengan jumlah byte terendah menggunakan yang terbaik dari pengkodean yang terlibat (yang mungkin akan menjadi UTF-8, kecuali mungkin Anda memprogramnya di APL). Saya minta maaf atas posting lama, tetapi banyak yang menjelaskan tentang pengkodean yang juga dapat diakses dalam spesifikasi resmi (pdf, bagian 3.9 D90 - D92) , atau Wikipedia .

Spesifikasi

Jika suatu saat bahasa pilihan Anda tidak dapat secara tepat memenuhi persyaratan, gantikan dengan sesuatu yang melekat pada semangat aturan yang diberikan. Misalnya. tidak setiap bahasa memiliki susunan bawaan, fungsi dll.

  • Tidak menggunakan string libraries / functions, atau encoding libraries / functions. Inti dari kode golf ini adalah untuk mengimplementasikan konverter menggunakan manipulasi bit / byte. Menggunakan string sendiri dalam kapasitasnya sebagai karakter atau byte array diperbolehkan. Oh, dan tidak ada panggilan OS yang melakukan konversi.

  • Konverter adalah fungsi yang akan mengambil tiga parameter: array byte yang mewakili string input yang dikodekan, dan pengkodean "input" dan "output" direpresentasikan sebagai angka. Secara sewenang-wenang kami akan menetapkan UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, and UTF32LEangka dari 0 hingga 6 dalam urutan itu. Tidak perlu memeriksa apakah nomornya < 0atau > 6, kami akan menganggap parameter ini benar. Konverter akan mengembalikan array byte yang valid dalam pengkodean output yang diinginkan.

  • Kami akan menggunakan karakter nol ( U+0000) sebagai terminator string. Apa pun setelah ini tidak masalah. Kami akan menganggap bahwa array input memiliki karakter nol di suatu tempat sehingga Anda tidak perlu melakukan pemeriksaan batas.

  • Sesuai FAQ , jika array byte input tidak valid untuk pengkodean yang dinyatakannya, kami harus memberi sinyal kesalahan. Kami akan melakukan ini dalam salah satu cara berikut: crash program, lempar pengecualian, kembalikan null atau kembalikan array yang empat byte pertamanya semuanya 0 (sehingga dapat dikenali U+0000dalam setiap penyandian).

Pengkodean

Spesifikasi resmi harus dipatuhi, tetapi Wikipedia memberikan penjelasan yang baik (dan sejauh yang saya yakini benar) dari pengkodean, dan saya akan meringkasnya di sini untuk kelengkapan. Perhatikan bahwa UTF-16 dan UTF-32 memiliki varian untuk endianness .

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

Pengkodean paling sederhana, setiap titik kode hanya dikodekan dalam 4 byte sama dengan nilai numeriknya. LE / BE mewakili endianness (little endian / big endian).

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

Poin kode dari U+0000 - U+FFFFdikodekan dalam 2 byte sama dengan nilai numeriknya. Nilai yang lebih besar dikodekan menggunakan sepasang pengganti yang merupakan nilai yang dicadangkan dari U+D800 - U+DFFF. Jadi untuk menyandikan poin lebih besar dari itu U+FFFF, algoritma berikut ini dapat digunakan (disalin tanpa malu-malu dari Wikipedia ):

  • 0x010000 dikurangi dari titik kode, meninggalkan angka 20 bit di kisaran 0..0x0FFFFF.
  • Sepuluh bit teratas (angka dalam kisaran 0..0x03FF) ditambahkan ke 0xD800 untuk memberikan unit kode pertama atau pengganti pengganti, yang akan berada dalam kisaran 0xD800..0xDBFF [...].
  • Sepuluh bit rendah (juga dalam kisaran 0..0x03FF) ditambahkan ke 0xDC00 untuk memberikan unit kode kedua atau pengganti jejak, yang akan berada dalam kisaran 0xDC00..0xDFFF [...].

UTF-8

Poin kode dari U+0000 - U+007Fdikodekan sebagai 1 byte sama dengan nilai numeriknya. Dari U+0080 - U+07FFmereka dikodekan sebagai 110xxxxx 10xxxxxx, U+0800 - U+FFFFadalah 1110xxxx 10xxxxxx 10xxxxxx, nilai yang lebih tinggi 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. Ini xadalah bit dari nilai numerik dari titik kode.

BOM

Tanda byte-order (BOM, U+FEFF) digunakan sebagai titik kode pertama untuk menunjukkan endianness. Mengikuti pedoman FAQ tentang BOM, BOM akan digunakan sebagai berikut: Untuk UTF-8, UTF-16 and UTF-32itu opsional. Jika BOM tidak ada di UTF-16atau UTF-32, diasumsikan sebagai big endian. BOM tidak boleh muncul diUTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE .

Kesalahan Umum Menyebabkan UTF Tidak Valid

Berbagai hal dapat menyebabkan urutan byte menjadi UTF tidak valid.

  • UTF-8 dan UTF-32: Langsung menyandikan poin kode pengganti ( U+D800 - U+DFFF), atau poin kode lebih besar dari U+10FFFF.
  • UTF-8: Banyak urutan byte yang tidak valid.
  • UTF-16: pengganti yang berpasangan atau tidak berpasangan.
  • BOM: Harus digunakan seperti yang ditentukan di bagian penyandian. Perhatikan bahwa ketika mengeluarkan UTF-16atau UTF-32(tidak ditentukan endianness inheren) Anda dapat memilih, tetapi dengan sedikit endian, Anda harus memasukkan BOM.

Perhatikan bahwa non-karakter dan titik kode yang tidak ditetapkan (keduanya berbeda dari pengganti) harus diperlakukan seperti karakter biasa.

DPenner1
sumber
"Tidak menggunakan string libraries / functions, atau encoding libraries / functions." Bagaimana dengan built-in yang sebenarnya. Di APL ''⎕R''⍠'InEnc' 'UTF16BE' 'OutEnc' 'UTF8-BOM',.
Adám
2
@NBZ Mereka tidak akan diizinkan karena tujuan dari tantangan ini adalah untuk menerapkan perilaku yang mereka berikan.
DPenner1
Catatan untuk penjawab: Saya telah sedikit banyak meninggalkan pertanyaan ini, tetapi mengingat minat baru-baru ini, dalam beberapa hari mendatang, saya akan meluangkan waktu untuk memeriksa jawabannya.
DPenner1

Jawaban:

3

C ++, (UTF-8) 971 byte

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

Program yang dapat dibaca di bawah ini dapat diringkas ke formulir di atas dengan memfilternya melalui perintah Perl berikut:

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'

Perintah di atas

  • menghapus komentar
  • menghapus spasi yang tidak perlu
  • mengubah heksadesimal literal menjadi desimal
  • mengembalikan baris baru di sekitar #includegaris

Kode yang bisa dibaca

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

Fungsi yang akan dipanggil adalah t(), dengan pengkodean input dan output diteruskan dalam variabel global idan omasing - masing, dan pmenunjuk pada byte input, yang harus diakhiri null. qmenunjuk ke buffer output, yang akan ditimpa, dan harus cukup besar untuk hasilnya - tidak ada upaya untuk menghindari buffer overrun.

Saya harap komentar kode cukup jelas - tanyakan di bawah ini apakah salah satunya terlalu samar (tapi usahakan dulu!).

Saya menyusun suite uji substansial sambil mengembangkan jawaban ini; Saya memasukkannya di bawah ini untuk kepentingan pendatang lain, dan untuk mendokumentasikan interpretasi saya tentang persyaratan:

Fungsi tes

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

Suite uji

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;
}
Toby Speight
sumber
Sial .. C ++ kalahkan Python.
Centangock
5

Python - 1367 karakter UTF-8

Baik! Ini adalah pertanyaan yang sangat sulit karena banyaknya pekerjaan yang diperlukan untuk memahami dan mengimplementasikan semua spesifikasi, tetapi saya pikir saya memiliki implementasi yang benar.

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]

convertadalah fungsi yang mengambil objek data 'bytes', ID input, dan ID output. Tampaknya bekerja - meskipun python tampaknya memiliki penggunaan BOM yang sedikit rusak ketika tidak ditentukan dalam pengkodean, jadi menggunakan pengkodean builtin python untuk menguji mode 1 dan 4 tidak akan berfungsi.

Fakta menyenangkan: Ukurannya juga 555 16 atau 10101010101 2 .

773 karakter untuk decoding, 452 untuk encoding, 59 untuk verifikasi dan 83 untuk bagian lain-lain.

Cel Skeggs
sumber
@ TrangOul: Secara umum, pengeditan sepele (seperti penandaan bahasa) disukai.
Zach Gates
Pertanyaan / jawaban itu tidak menunjukkan konsensus komunitas. Di seluruh jaringan, pengeditan sepele seperti ini disukai. Baik <1000 atau> 1000 pengguna rep harus melakukan pengeditan ini kecuali mereka jelas meningkatkan konten atau format. Yang terbaik adalah menunda mengedit hal-hal seperti tag bahasa, koreksi / perubahan satu kata, dll. @Cat
Zach Gates
Saya pikir ukurannya tidak lagi 0x555 :-(. Tetapi Anda mungkin lebih dekat menggunakan tip standar Python-golf menggunakan satu ruang untuk indentasi.
Toby Speight
@TobySpeight 0x557 sekarang, Anda benar. Dan sebenarnya, saya menggunakan tab, yang harus dikonversi menjadi spasi untuk dikirim, tetapi masih dihitung sebagai satu karakter. Saya akan pergi melihat apakah saya bisa mencukur beberapa karakter dengan cara lain ketika saya mendapat kesempatan.
Cel Skeggs
4

Python 3, 1138 byte (UTF-8)

Jadi ternyata 14 jam perjalanan internasional adalah kesempatan luar biasa untuk menyelesaikan tantangan golf ...

Fungsi konversi adalah C(). Ini panggilan u(), v()dan w()untuk decode, dan U(), V(), dan W()untuk encode, UTF-8, -16 dan -32, masing-masing. Tidak satu pun dari penyandi akan menghasilkan BOM, tetapi semua dekoder akan menangani dengan benar. Kondisi kesalahan menghasilkan pengecualian (biasanya a ZeroDivisionError, berkat fungsi "mati-tiba-tiba" 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)
Tim Pederick
sumber