Bộ chuyển đổi Unicode UTF


17

Mục tiêu là tạo ra một trình chuyển đổi tuân thủ đầy đủ giữa các bảng mã Unicode chính thức như được nêu trong Câu hỏi thường gặp về UTF . Vì đây là trung tâm của Unicode, tôi sẽ chấp nhận câu trả lời với số byte thấp nhất bằng cách sử dụng mã hóa tốt nhất có thể có (có lẽ sẽ là UTF-8, trừ khi bạn có thể lập trình nó trong APL). Tôi xin lỗi vì bài viết dài, nhưng rất nhiều trong số đó đang giải thích các bảng mã cũng có thể được truy cập trong thông số kỹ thuật chính thức (pdf, phần 3.9 D90 - D92) hoặc Wikipedia .

Thông số kỹ thuật

Nếu bất cứ lúc nào ngôn ngữ bạn chọn không thể đáp ứng chính xác một yêu cầu, hãy thay thế nó bằng thứ gì đó khắc phục tinh thần của các quy tắc được đưa ra. Ví dụ. không phải ngôn ngữ nào cũng có các mảng, hàm tích hợp, v.v.

  • Không sử dụng thư viện / hàm chuỗi hoặc mã hóa thư viện / hàm. Điểm của golf mã này là thực hiện trình chuyển đổi bằng cách sử dụng thao tác bit / byte. Mặc dù, việc sử dụng các chuỗi trong khả năng của họ như là một ký tự hoặc mảng byte được cho phép. Ồ, và không có cuộc gọi hệ điều hành nào thực hiện chuyển đổi.

  • Bộ chuyển đổi là một hàm sẽ có ba tham số: một mảng byte đại diện cho chuỗi đầu vào được mã hóa và mã hóa "đầu vào" và "đầu ra" được biểu diễn dưới dạng số. Tùy ý chúng tôi sẽ gán các UTF-8, UTF-16, UTF-16BE, UTF-16LE, UTF-32, UTF-32BE, and UTF32LEsố từ 0 đến 6 theo thứ tự đó. Không cần phải kiểm tra xem số đó có phải < 0hay không > 6, chúng tôi sẽ cho rằng các tham số này là chính xác. Bộ chuyển đổi sẽ trả về một mảng byte hợp lệ trong mã hóa đầu ra mong muốn.

  • Chúng ta sẽ sử dụng ký tự null ( U+0000) làm dấu kết thúc chuỗi. Bất cứ điều gì sau đó không quan trọng. Chúng tôi sẽ giả định rằng mảng đầu vào có ký tự null ở đâu đó để bạn không cần thực hiện kiểm tra giới hạn.

  • Theo Câu hỏi thường gặp , nếu mảng byte đầu vào không hợp lệ cho mã hóa được khai báo, chúng ta phải báo hiệu lỗi. Chúng tôi sẽ thực hiện điều này theo một trong các cách sau: đánh sập chương trình, ném ngoại lệ, trả về null hoặc trả về một mảng có bốn byte đầu tiên đều bằng 0 (để có thể nhận ra nó như U+0000trong mọi mã hóa).

Các bảng mã

Các thông số kỹ thuật chính thức phải được tuân theo, nhưng Wikipedia cung cấp một lời giải thích tốt (và theo như tôi tin là chính xác) về các mã hóa, và tôi sẽ tóm tắt chúng ở đây cho đầy đủ. Lưu ý rằng UTF-16 và UTF-32 có các biến thể cho tuổi thọ .

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

Mã hóa đơn giản nhất, mỗi điểm mã được mã hóa đơn giản bằng 4 byte bằng giá trị số của nó. LE / BE đại diện cho endianness (endian nhỏ / endian lớn).

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

Điểm mã từ U+0000 - U+FFFFđược mã hóa thành 2 byte bằng giá trị số của nó. Các giá trị lớn hơn được mã hóa bằng một cặp thay thế là các giá trị dành riêng từ đó U+D800 - U+DFFF. Vì vậy, để mã hóa các điểm lớn hơn U+FFFF, thuật toán sau có thể được sử dụng (được sao chép một cách không biết xấu hổ từ Wikipedia ):

  • 0x010000 bị trừ khỏi điểm mã, để lại một số 20 bit trong phạm vi 0..0x0FFFFF.
  • Mười bit trên cùng (một số trong phạm vi 0..0x03FF) được thêm vào 0xD800 để cung cấp đơn vị mã đầu tiên hoặc thay thế dẫn đầu, sẽ nằm trong phạm vi 0xD800..0xDBFF [...].
  • Mười bit thấp (cũng trong phạm vi 0..0x03FF) được thêm vào 0xDC00 để cung cấp đơn vị mã thứ hai hoặc thay thế dấu vết, sẽ nằm trong phạm vi 0xDC00..0xDFFF [...].

UTF-8

Điểm mã từ U+0000 - U+007Fđược mã hóa thành 1 byte bằng giá trị số của nó. Từ U+0080 - U+07FFchúng được mã hóa như 110xxxxx 10xxxxxx, U+0800 - U+FFFF1110xxxx 10xxxxxx 10xxxxxx, giá trị cao hơn là 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx. Các x's là các bit từ giá trị số của các điểm mã.

BOM

Dấu thứ tự byte (BOM, U+FEFF) được sử dụng làm điểm mã đầu tiên để biểu thị tuổi thọ. Theo hướng dẫn FAQ về BOM , BOM sẽ được sử dụng như sau: Đối với UTF-8, UTF-16 and UTF-32nó là tùy chọn. Nếu BOM vắng mặt trong UTF-16hoặc UTF-32, nó được coi là endian lớn. BOM không được xuất hiện trong UTF-16LE, UTF-16BE, UTF-32LE and UTF-32BE.

Cạm bẫy thường gặp gây UTF không hợp lệ

Nhiều thứ có thể khiến một chuỗi byte là UTF không hợp lệ.

  • UTF-8 và UTF-32: Mã hóa trực tiếp các điểm mã thay thế ( U+D800 - U+DFFF) hoặc điểm mã lớn hơn U+10FFFF.
  • UTF-8: Nhiều chuỗi byte không hợp lệ.
  • UTF-16: Các cặp thay thế được ghép đôi hoặc ghép cặp không đúng cách.
  • BOM: Phải được sử dụng như được chỉ định trong phần mã hóa. Lưu ý rằng khi xuất UTF-16hoặc UTF-32(không có chỉ định cuối cùng được chỉ định), bạn có thể chọn, nhưng với ít endian, bạn phải bao gồm BOM.

Lưu ý rằng các điểm mã không được ký tự và không được gán (cả hai khác biệt với các đại diện thay thế) sẽ được coi như các ký tự thông thường.


"Không sử dụng thư viện / hàm chuỗi hoặc mã hóa thư viện / hàm." Những gì về xây dựng thực sự. Trong APL , ''⎕R''⍠'InEnc' 'UTF16BE' 'OutEnc' 'UTF8-BOM'.
Adám

2
@NBZ Những người sẽ không được phép vì quan điểm của thách thức này là thực hiện hành vi mà những người đó cung cấp.
DPenner1

Lưu ý cho người trả lời: Tôi đã ít nhiều từ bỏ câu hỏi này, nhưng được quan tâm đổi mới gần đây, trong những ngày tới, tôi sẽ dành thời gian để xem qua các câu trả lời.
DPenner1

Câu trả lời:


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

Chương trình có thể đọc được dưới đây có thể được cô đọng lại ở dạng trên bằng cách lọc nó thông qua lệnh Perl sau:

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'

Lệnh trên

  • xóa bình luận
  • xóa các khoảng trắng không cần thiết
  • chuyển đổi chữ thập lục phân thành số thập phân
  • khôi phục dòng mới xung quanh #includedòng

Mã dễ đọc

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

Hàm được gọi là t(), với các mã hóa đầu vào và đầu ra được truyền trong các biến toàn cục iotương ứng, và ptrỏ vào các byte của đầu vào, phải được kết thúc bằng null. qtrỏ đến bộ đệm đầu ra, sẽ được ghi đè và phải đủ lớn cho kết quả - không có nỗ lực nào để tránh tràn bộ đệm.

Tôi hy vọng các nhận xét mã là đủ giải thích - hãy hỏi bên dưới nếu một trong số chúng quá khó hiểu (nhưng hãy nỗ lực trước!).

Tôi đã biên soạn một bộ kiểm tra đáng kể trong khi phát triển câu trả lời này; Tôi bao gồm nó dưới đây vì lợi ích của những người tham gia khác và để ghi lại các yêu cầu của tôi:

Chức năng kiểm tra

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

Bộ kiểm tra

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 ++ đánh bại Python.
TickTock

5

Python - 1367 ký tự UTF-8

Ổn thỏa! Đây là một câu hỏi cực kỳ khó vì khối lượng công việc cần thiết để hiểu và thực hiện tất cả các thông số kỹ thuật, nhưng tôi nghĩ rằng tôi đã thực hiện đúng.

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]

convertlà hàm lấy đối tượng 'byte' dữ liệu, ID đầu vào và ID đầu ra. Nó dường như hoạt động - mặc dù python dường như sử dụng BOM hơi bị hỏng khi không được chỉ định trong mã hóa, do đó, sử dụng mã hóa dựng sẵn của python để kiểm tra chế độ 1 và 4 sẽ không hoạt động.

Sự thật thú vị: Kích thước cũng là 555 16 hoặc 10101010101 2 .

773 ký tự để giải mã, 452 cho mã hóa, 59 để xác minh và 83 cho các phần linh tinh.


@TrangOul: Nói chung, các chỉnh sửa tầm thường (như gắn thẻ ngôn ngữ) được tán thành.
Zach Gates


Câu hỏi / câu trả lời đó không thể hiện sự đồng thuận của cộng đồng. Trên mạng, những chỉnh sửa tầm thường như thế này được tán thành. Cả <1000 hay> 1000 người dùng đại diện đều không nên thực hiện các chỉnh sửa này trừ khi họ cải thiện rõ ràng nội dung hoặc định dạng. Tốt nhất là không nên chỉnh sửa những thứ như thẻ ngôn ngữ, chỉnh sửa / thay đổi từ đơn, v.v ... @cat
Zach Gates

Tôi nghĩ rằng kích thước không còn là 0x555 :-(. Nhưng bạn có thể tiến gần hơn bằng cách sử dụng mẹo chơi gôn Python tiêu chuẩn để sử dụng một khoảng trống cho các vết lõm.
Toby Speight

@TobySpeight là 0x557 ngay bây giờ, bạn đã đúng. Và thực tế, tôi đã sử dụng các tab, phải được chuyển đổi thành khoảng trắng để đăng, nhưng vẫn được tính là một ký tự. Tôi sẽ đi xem liệu tôi có thể cạo một vài nhân vật theo cách khác khi tôi có cơ hội không.
Cel Skeggs

4

Python 3, 1138 byte (UTF-8)

Vì vậy, hóa ra 14 giờ du lịch quốc tế là một cơ hội tuyệt vời để kết thúc một thử thách chơi gôn ...

Hàm chuyển đổi là C(). Đây gọi u(), v()w()để giải mã, và U(), V()W()để mã hóa, UTF-8, -16 và -32, tương ứng. Không có bộ mã hóa nào sẽ tạo ra BOM, nhưng tất cả các bộ giải mã sẽ xử lý chính xác một bộ mã hóa. Các điều kiện lỗi dẫn đến một ngoại lệ (thường là ZeroDivisionError, do chức năng "đột ngột" 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.