빠르고 쉬운 UTF-8

14806 단어 cppjavascript

텍스트는 보기보다 간단합니다!



모든 개발자는 조만간 텍스트로 작업하는 벽에 부딪히게 되며, 텍스트를 올바르게 조작하는 복잡성에 뛰어들면 사람들이 쉽게 겁을 먹을 수 있습니다.

왜 우리는 좋은 것을 가질 수 없습니까?



유니코드는 표준 표현을 제공하여 텍스트 작업을 크게 단순화하지만 모든 곳에서 사용되는 것은 아닙니다. UTF-8은 가장 널리 사용되는 문자 인코딩 방식이지만 짐작하셨겠지만 모든 곳에서 사용되는 것은 아닙니다. 역사적인 이유로 UTF-16은 JavaScript, Java, C#, Windows, Qt 및 ICU 프로젝트의 기본값으로 남아 있습니다. 자세한 내용은 http://utf8everywhere.org/를 참조하십시오.

UTF-8 대 ASCII, UTF-16, UTF-32



UTF-8로 이동하기 전에 다른 인코딩 체계와의 빠른 비교는 다음과 같습니다.
  • UTF-8은 ASCII와 역호환되므로 ASCII의 모든 항목이 이미 UTF-8에 있습니다
  • .
  • ASCII는 128자만 인코딩하며, 이는 v13.0의 유니코드 143,859개
  • 의 작은 부분입니다.
  • UTF-8은 1~4개의 8비트 코드 단위를 사용하여 유니코드에 추가할 수 있는 충분한 공간을 제공합니다
  • .
  • UTF-16은 1개 또는 2개의 16비트 코드 단위를 사용합니다
  • .
  • UTF-32가 단일 32비트 코드 단위를 사용함

  • 대부분의 경우 UTF-16이 안전하지 않습니까?



    아니요. two most frequent emoji used in Twitter , ❤️ 및 😂를 고려하십시오. 대부분의 문자는 길이가 1인 것으로 취급되지만 이 이모티콘은 모두 두 개의 UTF-16 단위를 차지합니다. 자바스크립트의 예:



    UTF-32를 사용할 수 없습니까?



    UTF-32는 비효율적이며 텍스트를 저장하는 데 필요한 공간을 늘립니다. 일반적인 기대와 달리 UTF-32는 텍스트 조작을 위한 묘책도 아닙니다. 고정 너비이지만 단일 유니코드 코드 포인트만 나타내며 예를 들어 이모티콘과 같은 많은 문자는 코드 포인트의 조합으로 구성됩니다. 자바스크립트의 예:



    코드 포인트 ≠ 문자



    많은 문자가 단일 코드 포인트로 표시될 수 있지만 여러 코드 포인트에 걸쳐 있는 많은 문자도 있습니다.

    예를 들어, 태국어에는 서로 다른 성조 및 모음 기호อ อี อี้ อู้가 있으며 모두 별도의 코드 포인트로 구성되며 별도로 입력하고 지울 수 있습니다.



    다양한 이모지는 조합과 변형으로 구성됩니다.



    이것이 코드 포인트가 아니라면 무엇입니까? 문자소 클러스터here에 대해 자세히 알아보십시오.

    UTF-8은 어떻게 저장됩니까?



    UTF-8은 문자를 1~4바이트로 인코딩하고 접두사 비트를 사용하여 구분합니다. U+0000..U+10FFFF 범위의 모든 문자를 인코딩할 수 있습니다(UTF-16 범위로 제한됨).

    (x represents code point bits)
    0xxx-xxxx                                  1-byte sequence, 7-bit value
    110x-xxxx 10xx-xxxx                        2-byte sequence, 11-bit value 
    1110-xxxx 10xx-xxxx 10xx-xxxx              3-byte sequence, 16-bit value
    1111-0xxx 10xx-xxxx 10xx-xxxx 10xx-xxxx    4-byte sequence, 21-bit value
    


    UTF-32로 변환:

    UTF-8                                   |  UTF-32
    ---------------------------------------------------------------------
    0ABC-DEFG                               |  0000-0000 0000-0000 0000-0000 0ABC-DEFG
    110A-BCDE 10FG-HIJK                     |  0000-0000 0000-0000 0000-0ABC DEFG-HIJK
    1110-ABCD 10EF-GHIJ 10KL-MNOP           |  0000-0000 0000-0000 ABCD-EFGH IJKL-MNOP
    1111-0ABC 10DE-FGHI 10JK-LMNO 10PQ-RSTU |  0000-0000 000A-BCDE FGHI-JKLM NOPQ-RSTU
    


    바이트 접두사:
  • 0 - 1바이트 시퀀스
  • 110 - 2바이트 시퀀스의 시작
  • 1110 - 3바이트 시퀀스의 시작
  • 11110 - 4바이트 시퀀스의 시작
  • 10 - UTF-8 연속 바이트

  • 잘 테스트된 오픈 소스 솔루션이 있다는 점을 감안할 때 처음부터 UTF-8 반복을 구현해야 하는 것은 그리 흔한 일이 아닙니다. 그러나 작동 방식을 이해하는 데 여전히 유용한 연습입니다. 다음은 C++에서 UTF-8 반복의 예입니다.

    constexpr auto UTF8UnitMasks = std::array{
        0b0011'1111, 0b0111'1111, 0b0001'1111, 0b0000'1111, 0b0000'0111};
    
    int getUTF8Prefix(uint8_t c) {
        if (c < 0b1000'0000) return 1; // 1-byte (ASCII)
        else if (c < 0b1100'0000) return 0; // continuation
        else if (c < 0b1110'0000) return 2; // 2-byte
        else if (c < 0b1111'0000) return 3; // 3-byte
        else if (c < 0b1111'1000) return 4; // 4-byte
        else return -1; // invalid
    }
    
    // Returns the current code point and increments textBegin to the next one
    int32_t nextUTF8(const char** textBegin, size_t size) {
        if (!textBegin || !size) return -1;
    
        auto& data = *reinterpret_cast<const unsigned char**>(textBegin);
        auto units = getUTF8Prefix(data[0]); // count code point units
        if (units < 1 || units > size) {
            ++data;
            return -1;
        }
    
        // verify all subsequent units are continuation bytes, getUTF8Prefix(c) == 0
        if (std::any_of(data + 1, data + units, getUTF8Prefix)) {
            ++data;
            return -1;
        }
    
        auto value = int32_t(data[0]) & UTF8UnitMasks[units];
        for (int i = 1; i < units; ++i) {
            value = (value << 6) + (data[i] & UTF8UnitMasks[0]);
        }
    
        data += units;
    
        // check for Unicode range and overlong encoding (e.g, ASCII in 2+ bytes)
        switch (units) {
            case 1: return value;
            case 2: return value >= (1 << 7) ? value : -1;
            case 3: return value >= (1 << 11) ? value : -1;
            case 4: return value >= (1 << 16) && value <= 0x10FFFF ? value : -1;
            default: return -1;
        }
    }
    
    void example() {
        auto text = std::string_view("สวัส\xFFดีครับ!"); // Hello in Thai + invalid
        for (auto begin = text.begin(); begin < text.end();) {
            std::cout << nextUTF8(&begin, text.end() - begin) << " ";
        }
        std::cout << std::endl;
        // Output: 3626 3623 3633 3626 -1 3604 3637 3588 3619 3633 3610 33
        //                             ^ 0xFF - invalid code point
    }
    


    이 게시물은 표면적인 정보일 뿐이지만 일부 기본 정보를 이해하는 데 도움이 될 것입니다.

    좋은 웹페이지 즐겨찾기