Czym jest Base62?
Base62 to sposób zapisu liczby wyłącznie znakami, które przetrwają wklejenie gdziekolwiek: dziesięć cyfr, 26 wielkich liter i 26 małych liter. Bez interpunkcji, spacji, znaku dopełnienia, niczego, co trzeba by zamienić na sekwencje ucieczki przed wpisaniem do adresu URL, nazwy pliku, ciasteczka czy klucza bazy danych. Dziesięć plus 26 plus 26 to 62 — stąd nazwa.
Wersja na 30 sekund: liczba 12345 zapisuje się jako 3D7, a 1234567890 jako 1LY7VK. Powrót działa tą samą operacją w odwrotnym kierunku i wymaga dokładnie tego alfabetu, który wytworzył kod — inny alfabet zamieni te same sześć znaków w inną liczbę, bez żadnego komunikatu o błędzie.
Ta strona wykonuje dwa powiązane zadania, a różnica między nimi jest ważniejsza niż cokolwiek innego na tej stronie:
- Tryb liczbowy traktuje wejście jako liczbę. Dziesiętny identyfikator, np.
1234567890, staje się 1LY7VK, a wiodące zera wejścia nie są częścią wartości, więc nie wracają. - Tryb bajtowy traktuje wejście jako ciąg bajtów. Bajty są odczytywane jako jedna ogromna liczba big-endian, a następnie dzielone przez 62 — to ta sama arytmetyka, ale dla innej wielkości. Tutaj liczba wiodących bajtów zerowych przetrwa, po jednym znaku zerowym na bajt.
Skąd bierze się 62 i co to kosztuje
62 nie jest potęgą dwójki i ten jeden fakt kształtuje wszystko inne. Znane kodowania budowane są przez cięcie wejścia na stałe grupy bitów: 4 bity drukują się jako jeden znak szesnastkowy, 6 bitów jako jeden znak Base64, 5 bitów jako jeden znak Base32. Base62 tego nie potrafi, bo żadna całkowita liczba bitów nie mieści się w znaku o 62 wartościach — 5 bitów nazywa tylko 32 wartości, 6 bitów 64. Dlatego Base62 idzie przez arytmetykę: odczytaj całe wejście jako jedną liczbę całkowitą i wydrukuj ją w podstawie 62.
Jeden znak niesie wtedy log2(62) = 5,954 bita, czyli bardzo blisko 6 w Base64, a w trybie dziesiętnym jeden znak zastępuje log62(10) = 1,792 cyfry dziesiętnej. Kilka konsekwencji wartych znajomości przed wyborem długości:
| Długość kodu | Wartości, które może nazwać | W praktyce |
| 4 znaki | 624 = 14 776 336 | Wystarcza dla licznika, który nie przekracza siedmiu cyfr, i mieści się w przestrzeni wielkości numeru telefonu. |
| 6 znaków | 626 = 56 800 235 584 | Typowy kształt kodu identyfikatora. Dziesięciocyfrowy dziesiętny identyfikator mieści się w sześciu znakach. |
| 7 znaków | 627 = 3 521 614 606 208 | Ponad zakres 32-bitowego licznika o dwa rzędy wielkości, w szerokości krótkiego slugu linku. |
| 11 znaków | 6211 = 52 036 560 683 837 093 888 | Więcej niż 264 = 18 446 744 073 709 551 616, więc pełna 64-bitowa liczba mieści się w jedenastu znakach. |
Odwrotnie: 1000 cyfr dziesiętnych wymaga około 558 znaków, a 4096 cyfr dziesiętnych — 2285. Oszczędność jest realna, ale ograniczona: Base62 skraca liczbę dziesiętną o około 44% i nigdy nie pokona najgęstszych drukowalnych kodowań, ponieważ 5,954 bita na znak to po prostu mniej niż 6,4 czy 6,5.
Tryb liczbowy: co dzieje się z wiodącymi zerami
Liczba całkowita ma jedną kanoniczną postać pisaną i zaczyna się od znaku zerowego alfabetu, czyli 0 w dwóch kolejnościach z cyframi na początku, a A w kolejności z literami na początku. Wszystko z lewej strony pierwszego istotnego znaku to dopełnienie, a dopełnienie nie jest częścią wartości: 00012345 i 12345 stają się oba 3D7. Jeśli potrzebna jest stała szerokość — na przykład siedmioznakowy kod, który ma stać w kolumnie — pole Dopełnij wynik do stałej szerokości powtarza znaki zerowe aż do osiągnięcia szerokości, więc 12345 przy szerokości 7 drukuje się jako 00003D7 w alfabecie cyfry-najpierw, a AAAADNH w alfabecie litery-najpierw. Dopełnienie jest kosmetyczne: wiodące znaki nic nie dodają do wartości, a dekodowanie obu ciągów zwraca 12345, bo 0 × 626 i 0 × 625 są zerem, niezależnie od tego, który znak zapisuje zero.
Dekodowanie ciągu z dopełnieniem też działa, a strona podaje, ile wiodących znaków było znakami zerowymi, tak aby różnica między „kod ma siedem znaków” a „wartość potrzebowała trzech znaków” nigdy nie była tajemnicą.
Tryb bajtowy: ta sama arytmetyka na ciągu bajtów
Tryb bajtowy odczytuje bajty wejścia jako jedną liczbę big-endian — cały ciąg bajtów traktowany jest jako pojedyncza liczba, bez względu na długość — i drukuje ją w podstawie 62. Ponieważ bajt ma 8 bitów, a znak nazywa 5,954 z nich, tryb bajtowy nie jest kompresją: potrzebuje około 134% rozmiaru wejścia, nieco gorzej niż Base64 z 133%. Dla 92-bajtowego przykładu z tabeli rozmiarów 92 bajty drukują się jako 124 znaki Base62, a Base64 również 124 (123 z nich przed dopełnieniem =). Base62 przegrywa z Base64 gęstością, a wygrywa bezpieczeństwem znaków — to kompromis, na który świadomie decydują się krótkie linki i kody zaproszeń.
Wiodące bajty zerowe są zachowywane i łatwo to źle zrozumieć w obu kierunkach. Dwa wiodące bajty zerowe, po których następuje 0x01, drukują się jako 001 w alfabecie cyfry-najpierw i jako AAB w alfabecie litery-najpierw, ponieważ wiodące znaki zapisują zero, po jednym na bajt zerowy. Ciąg bajtów składający się z pojedynczego 0x00 drukuje się jako pojedynczy znak 0, a ciąg z pojedynczym 0x01 jako 1. To jedyne miejsce, w którym tryb bajtowy i liczbowy wyglądają inaczej, mimo że działają tym samym algorytmem.
Koszt tego jest taki, że tryb bajtowy dziedziczy kwadratowy czas arytmetyki: 1024 bajty szacuje się na około 1/3 sekundy, a 2048 bajtów na około 1,3 sekundy — te same liczby podaje tabela limitów powyżej. Dlatego tryb bajtowy jest tu ograniczony do 2048 bajtów wychodzących i 2048 znaków wracających — 2048 bajtów drukuje się jako około 2751 znaków, więc około 1520 bajtów to największy ładunek, który przetrwa pełny cykl na tej stronie — i dlatego tryb bajtowy na tej stronie jest referencją dla małych wartości, a nie zamiennikiem dla Base64 lub Base91 dla prawdziwych plików.
Trzy alfabety i dlaczego alfabet to cała historia
Znaki alfabetu to ustalony ciąg, a wartość znaku to jego pozycja w tym ciągu. Przestawienie ciągu zmienia znaczenie każdego znaku. W powszechnym użyciu są trzy kolejności, a poniżej ta sama liczba zapisana trzema sposobami:
| Dziesiętnie | 0-9, potem A-Z, potem a-z | 0-9, potem a-z, potem A-Z | A-Z, potem a-z, potem 0-9 |
| 0 | 0 | 0 | A |
| 61 | z | Z | 9 |
| 62 | 10 | 10 | BA |
| 12 345 | 3D7 | 3d7 | DNH |
| 1 234 567 890 | 1LY7VK | 1ly7vk | BViHfU |
Jeśli czytać trzy ostatnie kolumny jako jeden alfabet, tworzą spójny schemat. Jeśli czytać je jako wymienne, wszystko się sypie, ponieważ dekodowanie jest ślepe na alfabet w najgorszy możliwy sposób: każdy znak wejścia należy do wszystkich trzech alfabetów, więc zły alfabet nie daje żadnego błędu, tylko inną liczbę.
| Kod | Odczytany jako 0-9 A-Z a-z | Odczytany jako 0-9 a-z A-Z | Odczytany jako A-Z a-z 0-9 |
| 3D7 | 12 345 | 13 957 | 211 665 |
| 1LY7VK | 1 234 567 890 | 1 624 950 792 | 48 723 527 772 |
Wielkość liter to drugi, ostrzejszy wariant tej samej pułapki. Base62 rozróżnia wielkość liter we wszystkich kolejnościach, więc zamiana kodu na małe litery po cichu go przepisuje: 1LY7VK to 1 234 567 890 w alfabecie cyfry-najpierw, a 1ly7vk to 1 624 950 792 w tym samym alfabecie — a w alfabecie cyfry-potem-małe to 1 234 567 890, ponieważ ta kolejność to dokładnie kolejność cyfry-najpierw z zamienionymi dwiema wielkościami liter. Znak zerowy też nie jest wspólny: dwie kolejności z cyframi na początku zapisują zero jako 0, a kolejność z literami na początku jako A, więc ten alfabet w ogóle nie zaczyna się od cyfry.
Base62 obok innych kodowań
| Kodowanie | Bity na znak | Rozmiar względem wejścia | Uwagi |
| Base16 (hex) | 4 | 200% | Dwa znaki na bajt, łatwe do odczytania, bez alfabetu do sprawdzania. |
| Base32 | 5 | 160% | Bez rozróżniania wielkości liter i bezpieczny przy dyktowaniu, najbardziej rozrzutny z grupy. |
| Base36 (0-9 a-z) | ~5,17 | ~155% | Tylko cyfry i małe litery, więc przetrwa potok zmieniający wielkość liter. 12345 to 9ix. |
| Base58 | ~5,86 | ~137% | Wybrany dla adresów, a nie dla gęstości: cztery mylące znaki są pomijane zamiast uporządkowane. Zobacz Base32 / Base58. |
| Base62 | ~5,95 | ~134% | Każdy znak to cyfra lub litera, więc nic nigdy nie jest zamieniane na sekwencje ucieczki. Gęstość mieści się między Base58 a Base64. |
| Base64 | 6 | 133% | Domyślny wszędzie, kosztem +, / i =, trzech znaków wymuszających escapowanie. Zobacz konwerter Base64. |
| Ascii85 / Base85 | 6,4 | 125% | 5 znaków na 4 bajty, ale dzieli przez 85, a jego alfabet wymaga cudzysłowów. Zobacz Ascii85. |
| Base91 | ~6,5 | ~123% | Najgęstszy z nich i ten, którego wynik jest najmniej bezpieczny do wklejenia bez escapowania. Zobacz Base91. |
Procenty to asymptotyczne koszty, zaokrąglone; zmierzone liczby znaków dla jednego prawdziwego wejścia 92-bajtowego znajdują się w tabeli rozmiarów wyżej na tej stronie. W trybie liczbowym porównanie działa odwrotnie, ponieważ wejściem jest dziesiętny tekst, a nie bajty: tam Base62 jest o około 44% krótszy niż cyfry, które zastępuje, a Base36 o około 36% krótszy, dlatego kod identyfikatora zwykle jest Base62 lub Base36, a nie Base58.
Czym Base62 nie jest
- To nie standard. Nie ma RFC ani specyfikacji. Żadna organizacja standaryzacyjna nigdy nie zdefiniowała Base62, dlatego kolejność alfabetu nie jest gwarantowana, a ta strona wypisuje wszystkie trzy kolejności zamiast po cichu wybierać jedną.
- To nie szyfrowanie. To zmiana podstawy, nic więcej. Każdy, kto zna alfabet, odwraca ją jednym przebiegiem; nie ma klucza, soli ani żadnej tajności.
- To nie kompresja. Tryb bajtowy zwiększa dane o około jedną trzecią. Tryb liczbowy tylko wygląda na kompresję, ponieważ dziesiętny tekst jest nieefektywnym zapisem liczby.
- Nie rozróżnia wielkości liter. Base32 można zapisać małymi literami i odczytać z powrotem; Base62 nie. Kod, który przechodzi przez system normalizujący wielkość liter, jest uszkodzony.
- Nie sprawdza samego siebie. Nie ma sumy kontrolnej ani znaku kontrolnego, więc jeden błędny znak daje poprawnie wyglądający wynik o innej wartości. Jeśli to ma znaczenie, kolejność Bitcoin ma sumę kontrolną — zobacz sekcję Base58Check na stronie Base32 / Base58.
- Nie ma stałej długości. Liczba znaków rośnie wraz z wartością, więc licznik zaczynający się od 1 i dochodzący do 627 przechodzi od jednego znaku do ośmiu. Gdy potrzebna jest stała kolumna, dopełnij wynik do stałej szerokości.
Pułapki, które naprawdę gryzą
- Alfabet to odpowiedź na dziewięć z dziesięciu zagadek. Jeśli inne narzędzie daje inny kod dla tej samej liczby, porównaj oba alfabety znak po znaku, zanim uzna się błąd. Tabela powyżej pokazuje tę samą wartość w trzech alfabetach i wszystkie trzy są poprawne.
- Zły alfabet zawodzi po cichu. Nie ma dla niego ścieżki błędu:
3D7 dekoduje się do 12 345, 13 957 albo 211 665, zależnie wyłącznie od tego, jaka kolejność jest założona. Sprawdź pierwszy znak, który najczęściej jest zerowym dopełnieniem, i sprawdź, czy alfabet ma cyfry, czy litery na początku. - Wiodące zera znaczą dwie różne rzeczy. W trybie liczbowym
00012345 i 12345 to ta sama liczba i drukują się identycznie. W trybie bajtowym wiodący bajt zerowy to dane i drukuje się jako dodatkowy znak zerowy. Usunięcie jednego z tych znaków zmienia ciąg bajtów. - Normalizacja wielkości liter niszczy kod. Adres e-mail, nazwa hosta czy ścieżka Windows mogą zostać zapisane małymi literami w trakcie transportu. Kod Base62 tego nie przetrwa — dlatego Base36 wciąż istnieje.
- Długie wejścia są wolne, nie tylko duże. Konwersja to arytmetyka na całym wejściu naraz, więc podwojenie długości wejścia czterokrotnie wydłuża czas. Ta strona odmawia wejść przekraczających zmierzone limity zamiast się zawieszać; tabela rozmiarów wyżej podaje zmierzone liczby.
- Ciąg Base62 to nie dowód czegokolwiek. Ponieważ prawie każdy ciąg cyfr i liter dekoduje się bez skargi, dowolny slug URL zdekoduje się do jakiejś liczby. Kod ma znaczenie tylko wtedy, gdy system, który go wydał, przechowuje też odwzorowanie.
Krótkie odpowiedzi
„Dlaczego inne narzędzie Base62 daje inny kod dla tej samej liczby?”
Ponieważ alfabet się różni. Base62 nie ma specyfikacji, z którą mógłby się nie zgadzać, więc implementacje wybierają kolejność: cyfry i wielkie litery najpierw, cyfry i małe litery najpierw albo litery przed cyframi. Przełącz wybór alfabetu na górze tej strony, aby zobaczyć wszystkie trzy odpowiedzi obok siebie.
„Czy Base62 jest bezpieczny w URL?”
Tak, we wszystkich trzech kolejnościach. Wszystkie 62 znaki to niezakodowane litery i cyfry, więc żaden nie jest rozwijany do trójki procentowej w zapytaniu i żaden nie kończy adresu URL, segmentu ścieżki ani atrybutu HTML. To cały powód, dla którego krótkie linki są w Base62, a nie w Base64.
„Który jest mniejszy, Base62 czy Base64?”
Dla bajtów Base64 jest odrobinę mniejszy: około 133% wejścia wobec około 134%. Dla 92-bajtowego przykładu powyżej oba remisują na 124 znakach. Dla liczb dziesiętnych Base62 skraca tekst o około 44% i tu leży praktyczny zysk.
„Czy mogę zdekodować ciąg Base64 tą stroną?”
Nie, i nie zgłosi tego jako błędu. Oba alfabety mają wspólne wszystkie 62 znaki, więc ciąg Base64 zwykle jest tu też „poprawnym” wejściem i po prostu dekoduje się do innej liczby. Do Base64 używaj Base64.
„Czy kod Base62 ma maksymalną długość?”
Nie w teorii algorytm nie ma ograniczenia. W praktyce ta strona zatrzymuje się na 4096 cyfrach dziesiętnych lub 2048 bajtach, bo arytmetyka za nią jest kwadratowa i karta zawiesiłaby się bez niczego na ekranie, co by to wyjaśniało. Zmierzone liczby znajdują się w tabeli limitów wyżej na tej stronie.
„Skąd wiem, że wynik na tej stronie jest poprawny?”
Naciśnij Samotest. Uruchamia wszystkie udokumentowane przykłady, każdą długość bajtową od 0 do 64 i rozrzut liczb dziesiętnych we wszystkich trzech alfabetach, porównując zwrócone wartości z wejściem, i ponownie sprawdza, czy wszystkie 62 znaki każdego alfabetu są bezpieczne w URL. Przycisk Przykład dodatkowo porównuje swoje trzy wyniki z wartościami zapisanymi w pliku danych tej strony, które zweryfikowano niezależną implementacją dużych liczb przechowywaną w narzędziach testowych tej witryny.
Standardy i autorstwo
Nie ma tu standardu do wdrożenia: Base62 to konwencja, a nie specyfikacja, a trzy alfabety wypisane powyżej to te, które pojawiają się w rzeczywistym oprogramowaniu. Ta strona implementuje go z użyciem dużych liczb całkowitych witryny, napisanych zwykłą arytmetyką na tablicach cyfr, dzięki czemu żaden rozmiar nie jest ograniczony typem liczby JavaScript.
Tekst strony został napisany dla tej witryny. Każdy przykład na tej stronie został wygenerowany przez kod samej strony, a następnie zweryfikowany przez drugą, niezależną implementację z użyciem natywnych dużych liczb, która jest przechowywana w narzędziach testowych tej witryny; ta sama implementacja zweryfikowała tabele przykładów i rozmiarów w pliku danych.