Kalkulator długości ciągu znaków

Wklej ciąg znaków i narzędzie podaje jego długość w czterech różnych formach: w stylu JavaScript – .length (jednostki kodowe UTF-16), punkty kodowe Unicode, grupy grafemów (co użytkownicy postrzegają jako jeden znak) oraz bajty UTF-8 (co rzeczywiście jest przechowywane w kolumnie bazy danych). Te wartości różnią się dla każdego ciągu zawierającego emoji, flagi lub znaki łączące – i właśnie ta różnica stanowi źródło wielu błędów.

Cztery znaczenia długości ciągu znaków

  1. 1

    Jednostki kodu UTF-16

    Wartość zwracana przez `str.length` w JavaScripcie: 1 dla większości znaków oraz 2 dla każdego punktu kodowego powyżej U+FFFF (np. emoji, pisma starożytne).

  2. 2

    Punkty kodowe

    Jeden na każdy skalar Unicode. Każde emoji to 1, natomiast sekwencje ZWJ obejmują ich kilka.

  3. 3

    Klastry grafemów

    To, co użytkownik nazywa „jednym znakiem". `🇺🇸` to dwa punkty kodowe, ale jeden grafem; `n̈` to również dwa punkty kodowe, ale jeden grafem.

  4. 4

    Bajty UTF-8

    To, co przechowuje Twoja baza danych: ASCII – po 1 bajcie; typowe znaki europejskie – 2 bajty; CJK – 3 bajty; większość emoji – 4 bajty.

Przykłady

Ciąg JS .length Punkty kodowe Grafemy Bajty UTF-8
hello 5 5 5 5
café 4 4 4 5
😀 2 1 1 4
🇺🇸 4 2 1 8
👨‍👩‍👧 (rodzina) 8 5 1 18
n̈ (n + diereza) 2 2 1 3

Dlaczego liczby się różnią

Ciągi znaków w JavaScript są zapisywane w UTF-16, dlatego punkt kodowy powyżej U+FFFF jest przechowywany jako para zastępcza – dwie jednostki kodowe UTF-16. Stąd "😀".length === 2. Użytkownicy tego nie znoszą, ponieważ traktują 😀 jako jeden znak.

Grafemy idą jeszcze dalej: flaga kraju to dwa punkty kodowe wskaźnika regionalnego, które użytkownik widzi jako jedną flagę. W JavaScript "🇺🇸".length === 4 – co wydaje się absurdalne, ale tak właśnie jest. Aby iterować po grafemach, użyj Intl.Segmenter (nowoczesne rozwiązanie), biblioteki grapheme-splitter albo obsłuż je ręcznie.

Bajty UTF-8: to, co przechowuje Twoja baza danych

Dla kolumny typu VARCHAR(255):

  • W MySQL utf8 (w rzeczywistości: utf8mb3) wartość 255 oznacza bajty; café zajmuje 5 bajtów, więc mieści się 51 kopii.
  • W MySQL utf8mb4 (prawdziwy UTF-8 obejmujący emoji) nadal są to bajty, ale kodowanie obsługuje sekwencje czterobajtowe.
  • W Postgres VARCHAR(255) wartość 255 odnosi się do znaków (punktów kodowych), a nie bajtów.
  • W SQL Server VARCHAR zależy to od sortowania (collation); NVARCHAR liczy 2-bajtowe jednostki UCS-2.

Jeśli określasz rozmiar pól bazy danych według widocznej dla użytkownika liczby znaków, dla bezpieczeństwa zastosuj w kolumnach UTF-8 regułę „liczba bajtów × 4” – każdy grafem może zajmować do 4 bajtów na punkt kodowy, a sekwencje ZWJ dokładają jeszcze więcej.

Liczenie znaków w stylu Twittera

Twitter liczy tweet według własnej reguły: zlicza punkty kodowe, ale emoji oraz ideogramy CJK liczą się jako 2. Tweet złożony w 100% ze znaków ASCII może mieć 280 znaków; tweet ze 140 emoji osiąga limit 140 „znaków”.

Liczenie SMS-ów: 160 znaków w 7-bitowym GSM. Każdy znak spoza GSM (á, é, ñ, emoji) przełącza kodowanie na UCS-2 i długość Twojej wiadomości spada do 70 znaków.

Praktyczne zastosowania

  • Dobór rozmiaru kolumny bazy danych – sprawdź liczbę bajtów przed napisaniem migracji.
  • Egzekwowanie limitów API – większość API liczy bajty, a nie znaki.
  • Walidacja formularzy – pokaż użytkownikowi dokładną liczbę znaków zgodną z jego oczekiwaniami (grafemy).
  • Debugowanie wydajności – dlaczego to wyrażenie regularne iteruje tak wolno? Ponieważ dane wejściowe są trzy razy dłuższe w jednostkach kodowych niż w grafemach.

Najczęściej zadawane pytania

To emoji jest sekwencją ZWJ łączącą kilka punktów kodowych (na przykład emoji rodziny to 7 punktów kodowych). Twitter liczy je jako 2 znaki według reguły wag Unicode, a Twój edytor pokazuje 1 grafem. Oba podejścia są technicznie poprawne – po prostu mierzą co innego.

W bazach danych UTF-8 ze względów bezpieczeństwa załóż po 4 bajty na każdy oczekiwany znak. Pole na 100 znaków (grafemów) powinno mieć typ VARCHAR(400) bajtów, a jeśli baza liczy w znakach, jak Postgres, użyj VARCHAR(100) z odpowiednią obsługą zestawu znaków.

W JavaScript zależy to od formy normalizacji: znak złożony NFC (U+00E1) ma długość 1, natomiast znak rozłożony NFD (U+0061 + U+0301) ma długość 2. Choć widoczny znak jest identyczny, sekwencje bajtów są różne.

Emoji przedstawiające rodzinę i zawód to długie sekwencje ZWJ. Czcionki bez pełnej obsługi renderują każdy punkt kodowy jako osobny glif, dlatego 👨‍💻 zamienia się w 👨+💻. Aktualizacja czcionki systemowej rozwiązuje ten problem.

Powiązane narzędzia

Narzędzie jest dostępne w innych językach