Portál AbcLinuxu, 30. dubna 2025 11:23
Aha, takže ty existující prostředky jsou pomalé, nebo se zasekávají na neznámých znacích? Nebylo by lepší je opravit?Kdo říkal zasekávají? Jenom možná pro účely webového serveru není vyhození výjimky úplně ten nejlepší způsob, jak se s takovým znakem popasovat. Na druhou stranu, tohle je spíš jen teorie, protože se těžko někomu podaří v Javě zadat znak, který nejde zakódovat v UTF-8.
Tím "řízením bufferování" myslíte co? Mám knihovní funkci, pošlu jí Java string, ona alokuje a vrátí UTF-8 buffer, který pak pošlu do socketu.A to je právě to, co nechci. Mám funkci, která odněkud načítá String, až ho bude mít 2 MB, tak ho pošlu knihovní funkci, ta naalokuje buffer 2,5 MB, do něj String převede a pak už konečně můžu začít něco odesílat do socketu. A uživatel u prohlížeče zatím může jít na kafe. To by pak všechny ty prostředky HTTP a serveru, které umožňují průběžné odesílání, byly k ničemu, ne?
To by pak všechny ty prostředky HTTP a serveru, které umožňují průběžné odesílání, byly k ničemu, ne?To přece spolu vůbec nesouvisí, můžu načíst/vygenerovat pár stovek bajtů, ty překódovat, odeslat (jako http chunked), a číst dál.. Pokud opravdu máte funkci která něco načítá a vrátí se teprve až bude mít 2MB, k žádnému "průběžnému" překódování a odesílání se stejně nedostanete. Vše co jsem tvrdil je že je blbost dělat cokoliv dodatečného (jako třeba kontrola výstupního bufferu) během zpracování každého znaku- není pro to důvod, a je to neefektivní.
To přece spolu vůbec nesouvisí, můžu načíst/vygenerovat pár stovek bajtů, ty překódovat, odeslat (jako http chunked), a číst dál..Takže nechám onu knihovní funkci pro každých pár stovek bajtů vytvořit buffer, překódovat, zahodit buffer. A pak se lidi jako vy začnou divit, že je to pomalé a že za to určitě může ta Java.
Vše co jsem tvrdil je že je blbost dělat cokoliv dodatečného (jako třeba kontrola výstupního bufferu) během zpracování každého znaku- není pro to důvod, a je to neefektivní.Vzhledem k tomu, že se v tom kódu žádná kontrola výstupního bufferu nedělá, nevím, o čem zde píšete.
OutputStreamWriter
(což by bylo asi nejblíž Cčkové implementaci s pointerem). Pokud by ten OutputStreamWriter nebyl výrazně pomalejší, stálo by za to kód přepsat (resp. vyhodit tu podmínku, která UTF-8 zpracovává jinak, jiná kódování než UTF-8 a ASCII už tímhle způsobem zpracovávána jsou). Dříve to tak ale bylo, ta změna na současný způsob asi má nějaký důvod…
OutputStreamWriter.read
OutputStream
, který implementuje Writer
. Používání dlouhých názvů, ze kterých je patrné, o co jde, patří k dobrým praktikám programování v Javě.
Takže nechám onu knihovní funkci pro každých pár stovek bajtů vytvořit buffer, překódovat, zahodit buffer. A pak se lidi jako vy začnou divit, že je to pomalé a že za to určitě může ta Java.Nevěřím že alokace prázdného bufferu plus jeho GC má režii srovnatelnou nebo větší než pár stovek iterací oné
for (int i..)
smyčky. I kdyby ano, samozřejmě by každý thread mohl recyklovat jeden dostatečně velký buffer.
Vzhledem k tomu, že se v tom kódu žádná kontrola výstupního bufferu nedělá, nevím, o čem zde píšete.Ale dělá.. sice jen ve větvi //2b, ale zato dvakrát
Nevěřím že alokace prázdného bufferu plus jeho GC má režii srovnatelnou nebo větší než pár stovek iterací oné for (int i..)
smyčky. I kdyby ano, samozřejmě by každý thread mohl recyklovat jeden dostatečně velký buffer.
Těch iterací se ale nelze zbavit. Daly by se oželet některé podmínky, za cenu výrazného zvýšení spotřeba paměti. vytváření a rušení objektů sice bylo hodně optimalizováno, ale zase je neustále alokovat a rušit pro každých pár znaků…
Recyklovat buffer pro thread už by šlo, jenže takováhle speciální funkce je potřeba tak málo, že se nevyplatí dávat jí do standardní knihovny. Její použití znamená, že je potřeba nějakou část hodně zoptimalizovat, a v takovém případě už každý radši napíše svou implementaci. Než zjistit, že by tu speciální knihovní metodu mohl použít, ale nevyhovuje mu způsob synchronizace onoho bufferu, nebo jeho velikost, nebo nemůže určit optimální odhad o kolik jej nafouknout oproti počtu znaků…
Ale dělá.. sice jen ve větvi //2b, ale zato dvakrátPro českou webovou stránku vychází nárůst UTF-8 oproti Latin-2 zhruba 1,03 násobek. Kvůli tomu vytvářet 6násobně velký buffer?Nejdřív se kouká jestli jsou volné aspoň 2 bajty, pak ještě jestli je dost místa pro zbytek chunku za (pro češtinu) nesmyslně optimistického předpokladu že ani jeden zbývající znak nebude multibyte.
break
uprostred cyklu vypada vazne zajimave... Ale ten by asi pismena nezdvojoval. Spis by se dalo predpokladat, ze semtam nejaky zmizi
break
tam nemá co dělat, je to pozůstatek zjednodušování. Už jsme ho vymazal. S tím breakem by ten program hlavně zůstal v nekonečné smyčce.
if (chunk-i>buffer.length-bytes) chunk=buffer.length-bytes+i+1;pripadne by tam slo doplnit jeste jednu jednicku, ale neni nutna:
if (chunk-i-1>buffer.length-bytes) chunk=buffer.length-bytes+i+1;
chunk
je počet znaků, které se se ještě vejdou do bufferu, pokud by všechny znaky byly jednobajtové. Na konci cyklu je to zároveň počet skutečně zapsaných znaků. Ta vámi uvedená podmínka testuje, zda se do bufferu ještě vejde chunk
znaků, pokud budou všechny jednobajtové, pokud ne, tak se chunk
zmenší. Pokud je to ale na konci bufferu, je to špatně – proměnná obsahuje hodnotu, kolik znaků by se dalo zapsat v příštím cyklu, ale příští cyklus se už neprovede, takže znak se sice do bufferu zapsal, ale v chunk
je hodnota, jakoby se už nevešel. Takže je nutné otestovat, zda už nejsme na konci bufferu a v tom případě už chunk
nesnižovat.
Aktuální kód vypadá takhle:
if (bytes+chunk-i-1>buffer.length) chunk-=1;(Respektive
chunk-=2
pro 3bajtové znaky atd.)
-1
ve vašem případě v podmínce být nemusí, správně jsou oba případy. Nechal jsem se nachytat na to, že ta podmínka se přece nemůže lišit o jedničku od správného řešení. Může, protože v případě, že 1. podmínka splěná je ale 2. splněná není je přiřazovací příkaz vlastně chunk = chunk
. Co je náročnější na CPU, zda odečtení jedničky, nebo přiřazení hodnoty, to už radši řešit nebudu V proměnné chunk je počet znaků, které se se ještě vejdou do bufferu, pokud by všechny znaky byly jednobajtové.No to prave ze ne. Asi nejvetsi pruser je totiz v tom, ze
chunk
v ruznych situacich obsahuje hodnoty vyjadrujici (a nekdy ani to ne) neco jineho. Tento kod
if (chunk-i>buffer.length-bytes) chunk=buffer.length-bytes+i;ve spojeni s podminkou ve
for
cyklu napr. ve vysledku vede k tomu, ze v pripade, kdy nam doslo misto v bufferu (buffer.length-bytes == 0
) se cyklus prerusi a do chunk se ulozi pocet prednych znaku (ale blbe). Takze minimalne vsechny tyhle if
y vyhodit a na konec for
u dat
if( bytes >= buffer.length ) { chunk = i+1; break; }Dale odstranit
if (chunk>buffer.length-bytes)
a rozseknout chunk
na dve promenne 'pocet zapsanych znaku' a 'maximalni pocet znaku k zapsani'. Aby to bylo alespon trochu k pochopeni.
chunk
znamená "maximální počet znaků, který po skončení cyklu bude zapsán v bufferu" (na konci cyklu to tedy je "počet znaků zapsaných v bufferu"), a že zároveň tedy chunk-i
je "maximální počet znaků, které se ještě do bufferu vejdou" platí v celém kódu. Problém je v tom, že i++
se provádí až na konci cyklu, ne hned v místě, kde se znak skutečně zapíše. A mezi místem, kde by se i
mělo aktualizovat a kde se i
ve skutečnosti aktualizuje, je ona problematická podmínka, kde se vyskytuje neaktualizované i
. Složitě počítat chunk, pokud je potřeba jej zkrátit, je zbytečné, např. dvoubajtový znak si vzal z bufferu o jeden znak navíc "proti plánu", takže stačí udělat chunk-=1
.
Asi nejčitelnější a zároveň optimalizovaný kód by tedy byl:
int i = 0; while (i < chunk) { int code = s[offset+i]; if ((code & 0xffffff80) == 0) { // 1b buffer[bytes++]=(byte)(code); i++; } else if((code&0xfffff800)==0) { // 2b if (buffer.length-bytes<2) { chunk=i; break; } buffer[bytes++]=(byte)(0xc0|(code>>6)); buffer[bytes++]=(byte)(0x80|(code&0x3f)); i++; if (chunk-i>buffer.length-bytes) chunk-=1; } …
plus mať overflow buffer, aby sa nemuselo "vracať"
Byte[] UCS4_to_utf8(int[] in) { int in_l = in.length, out_l = 0; Byte[] out = malloc(6 * in_l); for (int i = 0; i < in_l;) { int c = in[i++]; if (c & bflm == 0) // 1b out[out_l++] = c; else if (c & psvz == 0) // 2b out[out_l++] = c1, out[out_l++] = c2; .. else if (c & xyzy == 0) // 6b out[out_l++] = c1, out[out_l++] = c2, out[out_l++] = c3, out[out_l++] = c4, out[out_l++] = c5, out[out_l++] = c6; } return out.realloc(out_l); }Dokonce bych se ani nedivil kdyby systém nějak speciálně optimalizoval
realloc()
, který upravuje velikost bloku, který daný thread těsně předtím alokoval- myslím že postup malloc(hodně), process(), realloc(málo)
je docela běžný..
Zajímavej nápad, takhle to postupně skládat do stejného bufferu mě nenapadlo.. ale v nejhorším případě to může vést ke geometricky klesající posloupnosti, a poměrně hodně iteracím té vnější smyčky..Bál bych se toho samého, navíc pro čistě ASCII texty tam bude ta vnější smyčka pouze navíc, a i pro češtinu bych si tipnul, že overhead vnější smyčky bude větší, než to, co se ušetří u znaků //>>2b
Dokonce bych se ani nedivil kdyby systém nějak speciálně optimalizovalrealloc()
, který upravuje velikost bloku, který daný thread těsně předtím alokoval- myslím že postupmalloc(hodně), process(), realloc(málo)
je docela běžný..
malloc
a realloc
ale v Javě nenapíšete
// 3-6b
jsou unreachable. :) Ale to je jedno, třeba Baňáka, třeba 6x.. V čem je hlavní pointa je že počítač ani tak nezajímá kolik paměti alokujete, jako spíš kolik jí použijete. Takže když pro zpracování 2k znaků alokuju třeba 1MB buffer, ale zapíšu do něj jen 3kB, VM si vystačí s jednou stránkou fyzické paměti, a nepotřebuje někde vybagrovat všech 256.
if (chunk-i>buffer.length-bytes) chunk=buffer.length-bytes+i;Se mi zdála podezřelá už od počátku, protože: a) Mění proměnnou "chunk" použitou v řízení cyklu for. To je sám o sobě trik který také snižuje čitelnost (některé jazyky to ani neumožňují). Z nečitelnosti vznikají těžko hledatelné chyby. Ale ani toto není ten problém. b) Míchá proměnné s různými významy. Proměnná "i" je index na aktuálně zpracovávaný znak, zatímco "bytes" je index prvního volného bytu ve výstupním bufferu. A to je ten problém
length-=chunk; offset+=chunk;Posouvají ukazatel "offset" o počet zpracovaných znaků. Fígl je v tom, že konstrukce:
chunk=buffer.length-bytes+i;má do "chunk" uložit počet zpracovaných znaků na vstupu + zbývající místo na výstupu. To ale nedělá. Výpočet "buffer.length-bytes" udává správně volné místo, ale v "i" není počet zpracovaných znaků, nýbrž (jak jsem napsal výše) index na aktuálně zpracovávaný znak, který je však o 1 nižší. (Znaky jsou indexované od nuly - tj. pokud např. zpracovávám pátý znak, má index 4.) Do "chunk" se tak vloží hodnota o jednu menší (tedy bez aktuálního znaku). Pokud není v bufferu už žádné místo, cyklus se ukončí, tento poslední znak se vypíše, ale nezapočítá do posunu length a offset. V následující obrátce nadřízeného cyklu "while (length > 0)" se zpracuje ještě jednou. Jednoduchá oprava: místo:
----- for (int i = 0; i < chunk; i++) { int code = s[offset+i]; -----bych napsal:
----- int i=0; while (i < chunk) { int code = s[offset+i]; i++; -----Po této úpravě je "i" o jednu vyšší než předtím. Má tak význam "index na první nezpracovaný znak", tj. totéž co "počet načtených znaků". Krom toho místo cyklu for je použit while, u kterého se dají více předpokládat záludnosti v řídících proměnných
if (buffer.length-bytes<2) { chunk=i-1; break; }A to samé v nastavení délky chunku:
if (chunk-i>buffer.length-bytes) chunk=buffer.length-bytes+i-1;Mně osobně ale váš kód přijde ještě složitější na pochopení, než originál. Úplně se v něm ztrácí to, že
chunk
není jen počet zapsaných znaků v této obrátce (tenhle význam má až po ukončení cyklu for), ale zároveň chunk-i
znamená maximální počet znaků, které se do bufferu ještě vejdou, a zajišťuje to nepřetečení buffer
u. A budu-li detailista, váš kód přičte jedničku zbytečně i v případě, kdy se už celý znak do bufferu nevejde a cyklus se ukončí break
em -1
už je samozřejmě špatně, tam už je hodnota i
zvětšená správně. Ono by bylo vůbec nejlepší to i++
provést těsně před nebo těsně po buffer[bytes++]
, sice by to bylo v kódu zopakované několikrát, ale bylo by to na místě, kde skutečně k tomu zápisu znaku dojde.
if((code&0xffff0000)==0)
?!?
code je char
, a Java jej odjakživa pevně definuje (což je ohromná výhoda proti zpátečnickému C) jako uint16_t
, takže uvedený kód nemá smysl, neboť podmínka bude vždy neplatná.
Character.codePointAt()
. Opravíte to v Jetty nebo se o to mám postarat?
http://www.stylusstudio.com/xmldev/200003/post10200.html
http://www.ingrid.org/java/i18n/utf-16/
Jediné rozumné cesty jsou:
a) oficiálně nepodporovat codepoints nad 0xffff (fakticky současný stav)
b) změnit char na uint32_t (a potenciálně tak rozbít starý kód)
c) (varinta na b) udělat String plně transparentní (volitelně UCS-4 nebo UTF-8 při spouštění JVM), tj věci jako length(), substr(), indexování, atd by se pak nad UTF-8 stringy chovaly jako by neobsahovaly multibyte znaky.
Ale přecházet na UTF-16 je podle mě strašná blbost, i když uznávám že jde o cestu nejmenšího odporu, jenže přesně to je cesta která Javu přivedla od dobrého nápadu do té p****e kde je teď.
char
přejmenovali na Character
, takže Java programátoři mají možnost volby co kde použijí. Jinými slovy, byli požehnáni další dimenzí, do které mohou rozvíjet bugy ve svém kódu. :) Navíc, implementovat unicode znaky nikoliv jako literály ale jako instance nějaké třídy je fakt #@%&^@#^.. ále, co bych se rozčiloval...
java.lang.Character
je v Javě odjakživa (to znamená to "Since 1.0"). char
je pořád 16bitový, ale přibyla možnost číst Unicode CodePoints jako int
. Ono co s tím taky dělat jiného, když se Unicode neočekávaně rozrostlo nad 16 bitů. Znakový literál je stále char
, řetězcový literál je stále String
, String
je stále soubor char
ů, String.length()
je stále počet char
v řetězci, řetězce jsou stále uložené v UTF-16. Pouze přibyly metody pro práci se surrogates, tak jak je to ve Standardu Unicode. To že si tvůrci Unicode na začátku mysleli, že s 16 bity vystačí, a pak zjistili, že ne, není chyba žádného programovacího jazyka.
Character
- buď tam nejsou, nebo je ta tabulka míchá dohromady s konstantami, takže jsem je přehlédl.. Pokud je celá třída JEN zabalením nějakých statických metod pro práci se surrogates, pak nechápu proč si ten konstruktor bere argument, a jak mohou fungovat ty asi 3 nestatické metody, co tam jsou... nerozumím.
Character
má privátní datovou proměnnou value
typu char
.
value
. An object of type Character contains a single field whose type is char. JavaDoc že privátní věci nekomentuje? Ale vždyť uvedená věta zjevně přesně toto dělá: popisuje privátní field! Jaký je proboha smysl mít docela pokročilý nástroj pro (formálně silný- tj tabulka jmen, typů a signatur) popis veřejného API, v něm současně popisovat i privátní záležitosti, ale dělat to zpátečnicky úplně jiným (neformální anglické věty) způsobem?
Stále nechápu proč ten base typ je char
, proč to není int
. Nemá přece vůbec žádný smysl kopírovat proměnnou typu char do objektu který bude jen boxovat ten samý typ! Zato kdyby byl base typ pro Character
int
, mohl by mít konstruktor stejnou signaturou jako má teď codePointAt
, pojmul by všechny Unicode znaky, a API by bylo mnohem průhlednější a jednodušší. Jistě, duplikoval by se tím typ int
, ale mít v kódu formálně odlišeny numerické hodnoty od unicode codepointů má aspoň nějaký důvod. Nevím, přiznávám že proti Javě jsem dlouhodobě zaujatej, ale je fakt moc těžký nebýt zaujatej když prakticky všechno na co kouknu se mi jeví jako udělané zoufale blbě :(
Aha, máte pravdu, je to tam- akorát nevím jak jste přišel na to že onen field se jmenuje právě value
.
Dalo by se to zjistit pohledem do zdrojáků, já to zjistil pohledem do Outline View příslušné třídy v eclipse.
JavaDoc že privátní věci nekomentuje? Ale vždyť uvedená věta zjevně přesně toto dělá: popisuje privátní field! Jaký je proboha smysl mít docela pokročilý nástroj pro (formálně silný- tj tabulka jmen, typů a signatur) popis veřejného API, v něm současně popisovat i privátní záležitosti, ale dělat to zpátečnicky úplně jiným (neformální anglické věty) způsobem?Ve zdrojovém kódu je dobré komentovat i privátní věci, při generování JavaDocu si pak můžete vybrat úroveň podrobností, defaultně se privátní prvky, metody a třídy do JavaDocu negenerují.
Stále nechápu proč ten base typ jeJava má pro každý primitivní typ odpovídající třídu, která ten typ obaluje. Původně byla Java zamýšlena jako jazyk pro nejrůznější přístroje s nepříliš velkým výpočetním výkonem, primitivní typy byly zavedeny jako ústupek tomu nízkému výkonu – aby nebylo nutné i pro tyto typy alokovat "celý" objekt a nakonec jej zase rušit. Obalující typy jsou způsob, jak tyto primitivní typy přenést do světa OOP. Dnes už je jasné, že zavádět primitivní datové typy nebyla šťastná volba, ale na řešení už je pozdě – jedině udělat zcela novou Javu.char
, proč to neníint
. Nemá přece vůbec žádný smysl kopírovat proměnnou typu char do objektu který bude jen boxovat ten samý typ! Zato kdyby byl base typ proCharacter
int
, mohl by mít konstruktor stejnou signaturou jako má teďcodePointAt
, pojmul by všechny Unicode znaky, a API by bylo mnohem průhlednější a jednodušší. Jistě, duplikoval by se tím typint
, ale mít v kódu formálně odlišeny numerické hodnoty od unicode codepointů má aspoň nějaký důvod. Nevím, přiznávám že proti Javě jsem dlouhodobě zaujatej, ale je fakt moc těžký nebýt zaujatej když prakticky všechno na co kouknu se mi jeví jako udělané zoufale blbě :(
char
je základní typ a Character
je jeho objektový protějšek. Podobně má int
svůj objektový protějšek Integer
, byte
Byte
atd. Cahracter tedy není typ, který by primárně řešil problém s narostlým Unicode, ale je to typ pro boxing primitivního typu char
.
class Character
uváděl jako řešení problému s rozbitým char
-em, když jde jen o boxovaný char
. To opravdu Javovský programátor musí pro ukládání Unicode řetězců používat neprůhledné obezličky typu int[]
?
char
je znak v UTF-16, String je řetězec znaků UTF-16, programátor s nimi může normálně pracovat. Pokud programátor tyto znaky chce převést na UTF-32 znaky, poskytuje mu k tomu třída Character API. Přesně podle doporučení Unicode konsorcia.
$ grep "__WCHAR_TYPE__" /usr/lib/gcc/i386-redhat-linux/4.1.1/include/stddef.h #define __WCHAR_TYPE__ int typedef __WCHAR_TYPE__ wchar_t;
UTF-16 je naprostý nesmysl, který je paměťově neefektivní (ASCII na 2 bajty). A UCS-4 (vy tomu říkáte UTF-32, dobrá, asi půjde o to samé) používá pro Unicode prakticky všechno krom Javy.Takže UTF-16 se dvěma bajty na znak je paměťově neefektivní, ale UCS6 se 4 bajty na znak je správně? To, že má řetězcové API dále pracovat se znaky UTF-16, není vynález můj, ale doporučení konsorcia Unicode. Při zpracování textů vám je datový typ pro jeden znak celkem k ničemu. Text se kolekce znaků, řetězec, a pro práci s řetězci datový typ samozřejmě existuje (jmenuje se kupodivu String).
Q: How about using UTF-32 interfaces in my APIs? A: Except in some environments that store text as UTF-32 in memory, most Unicode APIs are using UTF-16. With UTF-16 APIs the low level indexing is at the storage or code unit level, with higher-level mechanisms for graphemes or words specifying their boundaries in terms of the code units. This provides efficiency at the low levels, and the required functionality at the high levels. If its ever necessary to locate the nth character, indexing by character can be implemented as a high level operation. However, while converting from such a UTF-16 code unit index to a character index or vice versa is fairly straightforward, it does involve a scan through the 16-bit units up to the index point. In a test run, for example, accessing UTF-16 storage as characters, instead of code units resulted in a 10× degradation. While there are some interesting optimizations that can be performed, it will always be slower on average. Therefore locating other boundaries, such as grapheme, word, line or sentence boundaries proceeds directly from the code unit index, not indirectly via an intermediate character code index. [MD] Q: Doesn’t it cause a problem to have only UTF-16 string APIs, instead of UTF-32 char APIs? A: Almost all international functions (upper-, lower-, titlecasing, case folding, drawing, measuring, collation, transliteration, grapheme-, word-, linebreaks, etc.) should take string parameters in the API, not single code-points (UTF-32). Single code-point APIs almost always produce the wrong results except for very simple languages, either because you need more context to get the right answer, or because you need to generate a sequence of characters to return the right answer, or both. For example, any Unicode-compliant collation (See Unicode Technical Stdandard #10: Unicode Collation Algogrithm (UCA)) must be able to handle sequences of more than one code-point, and treat that sequence as a single entity. Trying to collate by handling single code-points at a time, would get the wrong answer. The same will happen for drawing or measuring text a single code-point at a time; because scripts like Arabic are contextual, the width of x plus the width of y is not equal to the width of xy. Once you get beyond basic typography, the same is true for English as well; because of kerning and ligatures the width of “fi” in the font may be different than the width of “f” plus the width of “i". Casing operations must return strings, not single code-points; see http://www.unicode.org/charts/case/ . In particular, the title casing operation requires strings as input, not single code-points at a time. Storing a single code point in a struct or class instead of a string, would exclude support for graphemes, such as “ch” for Slovak, where a single code point may not be sufficient, but a character sequence is needed to express what is required. In other words, most API parameters and fields of composite data types should not be defined as a character, but as a string. And if they are strings, it does not matter what the internal representation of the string is. Given that any industrial-strength text and internationalization support API has to be able to handle sequences of characters, it makes little difference whether the string is internally represented by a sequence of UTF-16 code units, or by a sequence of code-points ( = UTF-32 code units). Both UTF-16 and UTF-8 are designed to make working with substrings easy, by the fact that the sequence of code units for a given code point is unique. [AF] & [MD] Q: Are there exceptions to the rule of exclusively using string parameters in APIs? A: The main exception are very low-level operations such as getting character properties (e.g. General Category or Canonical Class in the UCD). For those it is handy to have interfaces that convert quickly to and from UTF-16 and UTF-32, and that allow you to iterate through strings returning UTF-32 values (even though the internal format is UTF-16). [MD]
char
z uint16_t
na uint32_t
. Obě podmínky by zůstaly zachovány. Pokud by to rozbilo nějakou starou aplikaci (např by spoléhala že cast na char usekne z intu horních 16 bitů), byla by aplikace velmi pravděpodobně napsána chybně, a zasloužila by si to. Vývoj Javy nesleduji, a opravdu jsem se domníval že přechod Unicode z 16 na 21 bitů vyřešili rozšířením datového typu char
na 32 bitů. Poznámka že "char
je přece napevno 16 bitů" byla pokus o sakrasmus, který mi bohužel nevyšel. Inu, jsem nepolepšitelný optimista když předpokládám že lidi kolem Javy mají občas rozum.
Co se mi v těch textech o Javě ale OPRAVDU VELMI líbilo je jak tam zmiňují graphemes, such as Slovak letter "ch". To je fakt pobav, a klasická ukázka sebeklamu nebo rovnou aktivní demagogie. Tím že přešli na UTF-16 rozbili do té doby platný invariant že codepoint zabírá ve stringu vždy jednu pozici čímž vážně rozbili zpětnou kompatibilitu. Aby je developeři po právu za takovej kiks nezlynčovali, museli mamlasové z komitétu vymyslet nějaké zdůvodnění proč to vlastně nevadí. A tak kdesi vyštrachali že existuje něco jako dvojhlásky, jako "slovenské"(sic!) písmeno ch, a pro jeho detekci je třeba beztak koukat na string jako na celek, nikoliv jako na posloupnost unicode znaků, takže UTF-16 vlastně jen dělá něco co tu už stejně bylo. Hahahahahaha... Kdyby skutečně někomu záleželo na tom aby se dvouhláska ch chovala jako jeden element, jednoduše pro něj zavede Unicode codepoint, a je vymalováno. Ale protože to nikdo nepotřebuje, nestalo se tak, takže ch jako argument padá. Java je rozbitá, Q.E.D.
Jo, a už mě ta debata vážně nebaví.
Tiskni
Sdílej:
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.