Mam ciąg javascript, który jest wysyłany z serwera w UTF-8, który wynosi około 500K. Jak mogę określić jego rozmiar w JavaScript?
Wiem, że JavaScript używa UCS-2, więc oznacza to 2 bajty na znak. Czy jednak zależy to od implementacji JavaScript? Albo o kodowaniu strony, a może typie zawartości?
javascript
string
size
byte
Paul Biggar
źródło
źródło
Odpowiedzi:
String
wartości nie są zależne od implementacji, zgodnie ze specyfikacją ECMA-262 3rd Edition , każdy znak reprezentuje pojedynczą 16-bitową jednostkę tekstu UTF-16 :źródło
Ta funkcja zwróci rozmiar bajtu dowolnego przekazanego do niej ciągu znaków UTF-8.
function byteCount(s) { return encodeURI(s).split(/%..|./).length - 1; }
Źródło
Silniki JavaScript mogą wewnętrznie używać UCS-2 lub UTF-16. Większość silników, które znam, używa UTF-16, ale bez względu na wybór, jest to tylko szczegół implementacji, który nie wpłynie na charakterystykę języka.
Jednak sam język ECMAScript / JavaScript udostępnia znaki zgodnie z UCS-2, a nie UTF-16.
Źródło
źródło
.split(/%(?:u[0-9A-F]{2})?[0-9A-F]{2}|./)
zamiast tego. Twój fragment kodu nie działa w przypadku ciągów kodujących jako „% uXXXX”.Jeśli używasz node.js, istnieje prostsze rozwiązanie korzystające z buforów :
function getBinarySize(string) { return Buffer.byteLength(string, 'utf8'); }
Jest do tego biblioteka npm: https://www.npmjs.org/package/utf8-binary-cutter (od ciebie wiernie)
źródło
Możesz użyć obiektu Blob, aby uzyskać rozmiar ciągu w bajtach.
Przykłady:
console.info( new Blob(['😂']).size, // 4 new Blob(['👍']).size, // 4 new Blob(['😂👍']).size, // 8 new Blob(['👍😂']).size, // 8 new Blob(['I\'m a string']).size, // 12 // from Premasagar correction of Lauri's answer for // strings containing lone characters in the surrogate pair range: // https://stackoverflow.com/a/39488643/6225838 new Blob([String.fromCharCode(55555)]).size, // 3 new Blob([String.fromCharCode(55555, 57000)]).size // 4 (not 6) );
źródło
Buffer.from('😂').length
Wypróbuj tę kombinację z użyciem funkcji unescape js:
const byteAmount = unescape(encodeURIComponent(yourString)).length
Przykład pełnego kodowania:
const s = "1 a ф № @ ®"; // length is 11 const s2 = encodeURIComponent(s); // length is 41 const s3 = unescape(s2); // length is 15 [1-1,a-1,ф-2,№-3,@-1,®-2] const s4 = escape(s3); // length is 39 const s5 = decodeURIComponent(s4); // length is 11
źródło
unescape
JavaScript jest przestarzała i nie powinna być używana do dekodowania jednolitych identyfikatorów zasobów (URI). Źródłounescape
nie jest używany do dekodowania URI. Służy do konwersji%xx
sekwencji na pojedyncze znaki. AsencodeURIComponent
koduje łańcuch jako UTF-8, reprezentujący jednostki kodu jako odpowiadający mu znak ASCII lub jako%xx
sekwencję, wywołanieunescape(encodeURIComponent(...))
daje w wyniku ciąg binarny zawierający reprezentację oryginalnego ciągu w formacie UTF-8..length
Prawidłowe wywołanie podaje rozmiar w bajtach ciągu zakodowanego jako UTF-8.un
)escape
jest przestarzałe od 1999 roku, ale nadal jest dostępne w każdej przeglądarce ... - To powiedziawszy, jest dobry powód, aby go wycofać. W zasadzie nie ma sposobu, aby ich poprawnie używać (z wyjątkiem kodowania / dekodowania UTF8 w połączeniu zen
- /decodeURI
(Component
) - lub przynajmniej nie znam żadnej innej użytecznej aplikacji dla (un
)escape
). A dziś są lepsze alternatywy dla kodowania / dekodowania UTF8 (TextEncoder
itp.)Pamiętaj, że jeśli kierujesz reklamy na node.js, możesz użyć
Buffer.from(string).length
:var str = "\u2620"; // => "☠" str.length; // => 1 (character) Buffer.from(str).length // => 3 (bytes)
źródło
Oto 3 sposoby, których używam:
TextEncoder
new TextEncoder().encode("myString").length
Blob
new Blob(["myString"]).size
Buffer
Buffer.byteLength("myString", 'utf8')
źródło
TextEncoder
opcja jest ponad 6 razy szybsza: i.ibb.co/QkfsJQN/Screenshot-from-2020-12-20-16-29-27.pngUTF-8 koduje znaki przy użyciu od 1 do 4 bajtów na punkt kodowy. Jak wskazał CMS w zaakceptowanej odpowiedzi, JavaScript będzie przechowywać każdy znak wewnętrznie przy użyciu 16 bitów (2 bajty).
Jeśli przeanalizujesz każdy znak w ciągu za pomocą pętli i policzysz liczbę użytych bajtów na punkt kodowy, a następnie pomnożymy całkowitą liczbę przez 2, użycie pamięci JavaScript powinno być w bajtach dla tego zakodowanego ciągu UTF-8. Może coś takiego:
getStringMemorySize = function( _string ) { "use strict"; var codePoint , accum = 0 ; for( var stringIndex = 0, endOfString = _string.length; stringIndex < endOfString; stringIndex++ ) { codePoint = _string.charCodeAt( stringIndex ); if( codePoint < 0x100 ) { accum += 1; continue; } if( codePoint < 0x10000 ) { accum += 2; continue; } if( codePoint < 0x1000000 ) { accum += 3; } else { accum += 4; } } return accum * 2; }
Przykłady:
getStringMemorySize( 'I' ); // 2 getStringMemorySize( '❤' ); // 4 getStringMemorySize( '𠀰' ); // 8 getStringMemorySize( 'I❤𠀰' ); // 14
źródło
Rozmiar ciągu JavaScript to
Pre-ES6
Zawsze 2 bajty na znak. UTF-16 nie jest dozwolony, ponieważ specyfikacja mówi, że „wartości muszą być 16-bitowymi liczbami całkowitymi bez znaku”. Ponieważ łańcuchy UTF-16 mogą używać 3 lub 4-bajtowych znaków, naruszyłoby to wymaganie 2-bajtowe. Co najważniejsze, chociaż UTF-16 nie może być w pełni obsługiwany, standard wymaga, aby używane znaki dwubajtowe były poprawnymi znakami UTF-16. Innymi słowy, ciągi JavaScript w wersjach wcześniejszych niż ES6 obsługują podzbiór znaków UTF-16.
ES6 i później
2 bajty na znak lub 5 lub więcej bajtów na znak. Dodatkowe rozmiary wchodzą w grę, ponieważ ES6 (ECMAScript 6) dodaje obsługę ucieczki punktów kodowych Unicode . Użycie znaku ucieczki Unicode wygląda następująco: \ u {1D306}
Praktyczne notatki
Nie dotyczy to wewnętrznej implementacji konkretnego silnika. Na przykład niektóre silniki używają struktur danych i bibliotek z pełną obsługą UTF-16, ale to, co zapewniają zewnętrznie, nie musi w pełni obsługiwać UTF-16. Silnik może również zapewniać zewnętrzne wsparcie dla UTF-16, ale nie jest do tego upoważniony.
W przypadku ES6 praktycznie rzecz biorąc, znaki nigdy nie będą dłuższe niż 5 bajtów (2 bajty dla punktu ucieczki + 3 bajty dla punktu kodowego Unicode), ponieważ najnowsza wersja Unicode ma tylko 136755 możliwych znaków, co z łatwością mieści się w 3 bajtach. Jednak nie jest to technicznie ograniczone przez standard, więc w zasadzie pojedynczy znak może używać powiedzmy 4 bajty na punkt kodowy i łącznie 6 bajtów.
Większość przykładów kodu służących do obliczania rozmiaru bajtów wydaje się nie uwzględniać ucieczki punktu kodowego ES6 Unicode, więc w niektórych przypadkach wyniki mogą być nieprawidłowe.
źródło
Buffer.from('test').length
iBuffer.byteLength('test')
równa 4 (w węźle) inew Blob(['test']).size
również jest równa 4?'\u{1F600}'.length===2
,'\u{1F600}'==='\uD83D\uDE00'
,'\u{1F600}'==='😀'
)Pojedynczy element w ciągu JavaScript jest uważany za pojedynczą jednostkę kodu UTF-16. Oznacza to, że ciągi znaków są przechowywane w formacie 16-bitowym (1 jednostka kodu), a 16-bitowy jest równy 2 bajtom (8-bitów = 1 bajt).
charCodeAt()
Metoda może być zastosowana, aby powrócić liczbę całkowitą od 0 do 65535, przedstawiający urządzenie kodu UTF-16 przy danym wskaźniku.codePointAt()
Może być stosowany do całej wartości powrotu punkt kodowy znaki unikodowe np UTF-32.Gdy znak UTF-16 nie może być reprezentowany w pojedynczej 16-bitowej jednostce kodu, będzie miał parę zastępczą, a zatem użyje dwóch jednostek kodu (2 x 16-bit = 4 bajty)
Zobacz kodowania Unicode dla różnych kodowań i ich zakresów kodów.
źródło
Odpowiedź od Lauri Oherd działa dobrze dla większości ciągów występujących w środowisku naturalnym, ale zakończy się niepowodzeniem, jeśli ciąg zawiera pojedyncze znaki z zakresu par zastępczych, od 0xD800 do 0xDFFF. Na przykład
byteCount(String.fromCharCode(55555)) // URIError: URI malformed
Ta dłuższa funkcja powinna obsługiwać wszystkie ciągi:
function bytes (str) { var bytes=0, len=str.length, codePoint, next, i; for (i=0; i < len; i++) { codePoint = str.charCodeAt(i); // Lone surrogates cannot be passed to encodeURI if (codePoint >= 0xD800 && codePoint < 0xE000) { if (codePoint < 0xDC00 && i + 1 < len) { next = str.charCodeAt(i + 1); if (next >= 0xDC00 && next < 0xE000) { bytes += 4; i++; continue; } } } bytes += (codePoint < 0x80 ? 1 : (codePoint < 0x800 ? 2 : 3)); } return bytes; }
Na przykład
bytes(String.fromCharCode(55555)) // 3
Prawidłowo obliczy rozmiar ciągów zawierających pary zastępcze:
bytes(String.fromCharCode(55555, 57000)) // 4 (not 6)
Wyniki można porównać z wbudowaną funkcją Node
Buffer.byteLength
:Buffer.byteLength(String.fromCharCode(55555), 'utf8') // 3 Buffer.byteLength(String.fromCharCode(55555, 57000), 'utf8') // 4 (not 6)
źródło
Pracuję z wbudowaną wersją silnika V8. Przetestowałem pojedynczy ciąg. Wypychanie każdego kroku 1000 znaków. UTF-8.
Pierwszy test z jednobajtowym (8-bitowym, ANSI) znakiem „A” (szesnastkowo: 41). Drugi test ze znakiem dwubajtowym (16-bitowy) „Ω” (szesnastkowo: CE A9) i trzeci test ze znakiem trzy bajtowym (24-bitowy) „☺” (szesnastkowo: E2 98 BA).
We wszystkich trzech przypadkach urządzenie drukuje z pamięci przy 888 000 znaków i przy użyciu ok. 26 348 kb w pamięci RAM.
Wynik: znaki nie są przechowywane dynamicznie. I to nie tylko z 16-bitowym. - Ok, może tylko w moim przypadku (wbudowane 128 MB pamięci RAM, silnik V8 C ++ / QT) - Kodowanie znaków nie ma nic wspólnego z rozmiarem w pamięci RAM silnika javascript. Np. EncodingURI itp. Jest użyteczne tylko do wysokopoziomowej transmisji i przechowywania danych.
Osadzone czy nie, faktem jest, że postacie są przechowywane nie tylko w 16 bitach. Niestety nie mam 100% odpowiedzi, co robi Javascript na niskim poziomie. Przy okazji. Przetestowałem to samo (pierwszy test powyżej) z tablicą znaków „A”. Podano 1000 pozycji na każdym kroku. (Dokładnie ten sam test. Po prostu zamieniłem ciąg na tablicę) I system wyprowadził z pamięci (pożądany) po 10 416 KB i długości tablicy 1 337 000. Tak więc silnik javascript nie jest po prostu ograniczony. To bardziej złożone.
źródło
Możesz spróbować tego:
var b = str.match(/[^\x00-\xff]/g); return (str.length + (!b ? 0: b.length));
U mnie to zadziałało.
źródło