#define _POSIX_C_SOURCE 200809L #include #include #include /* V této ukázce se vrátíme k jednoduché práci s textem – naším * úkolem bude sestavit řetězec složený z prvních písmen každého * řádku. Nebudeme ovšem pracovat se skutečnými písmeny (to by bylo * pro účely tohoto předmětu příliš složité) – omezíme se na kódové * body.¹ Vstupní text budeme navíc uvažovat vždy v kódování * UTF-16BE. */ /* V kódování UTF-16: * * • znaky v rozsahu 0–0xd7ff a v rozsahu 0xe000–0xffff jsou * kódovány jedním dvoubajtovým číslem, * • znaky v rozsahu 0x010000 až 0x10ffff jsou kódovány dvojicí * dvoubajtových čísel – první, H, je z rozsahu 0xd800 až 0xdbff, * druhé, L, z rozsahu 0xdc00 až 0xdfff, přitom výsledný znak se * získá jako ( H - 0xd800 ) << 10 | ( L - 0xdc00 ) + 0x10000. */ /* Dekodér UTF (ať už 8 nebo 16), který má zpracovávat jednotlivé * bajty, vyžaduje «stav» – informaci o tom, v jaké fázi dekódování * se právě nachází. Velkou výhodou takto navrženého dekodéru je, že * bude bez problémů pracovat s libovolným druhem vstupu – zejména * není potřeba řešit situace, kdy je znak rozdělen mezi dvě čtení * (protože znaky mají proměnnou délku, žádná velikost bufferu * nemůže zabezpečit, že každé čtení načte pouze kompletní kódové * body; nemluvě o čtení z proudových socketů atp.). * * Tento stav zapouzdříme do datového typu ‹decode_state›. Položka * ‹index› určuje pořadové číslo bajtu (v rámci znaku), který * následuje. Hodnoty ‹l› a ‹h› jsou dekódovaná slova (odpovídá L a * H výše) a konečně ‹point› je výsledný kódový bod. */ struct decode_state { int index; uint16_t l, h; uint32_t point; }; /* Samotný dekodér realizuje podprogram ‹decode_byte›. Vstupem je * jednak bajt, který se má zpracovat (všimněte si, že jej při * předání rovnou převedeme na 32b číslo), jednak stav dekodéru. * Stav (resp. alespoň položku ‹index›) je před samotným startem * nutné vynulovat. */ /* Návratová hodnota bude: * * • 0 – byl zpracován kompletní kódový bod; volající si ho přečte * v položce ‹point› struktury ‹decode_state›, * • 1 – vstupní bajt byl úspěšně zpracován, ale kódový bod zatím * není kompletní, * • 2 – při dekódování došlo k chybě – vstup není platné UTF-16. * * Po zpracování posledního bajtu musí volající také dbát hodnoty * ‹index› – není-li nulová, znamená to, že na konci vstupu zbývají * bajty, které netvoří kompletní kódový bod. */ int decode_byte( uint32_t b, struct decode_state *s ) { switch ( s->index++ ) { case 0: s->h = b << 8; return 1; case 1: s->h |= b; if ( s->h <= 0xd7ff || s->h >= 0xe000 ) { s->index = 0; s->point = s->h; } return s->index == 0 ? 0 : 1; case 2: s->l = b << 8; return 1; case 3: s->l |= b; if ( s->l < 0xdc00 ) return 2; s->index = 0; s->point = ( s->h - 0xd800 ) << 10 | ( s->l - 0xdc00 ) + 0x10000; return 0; } assert( 0 ); } /* Hlavní část řešení bude představovat funkce ‹acrostic›, která na * vstupu obdrží kódování řetězce a jeho délku, nalezne první * písmeno každého řádku a tato uloží do výstupního parametru * ‹output›. Výsledkem bude počet kódových bodů, které by bylo * potřeba k zápisu všech prvních písmen. Je-li tento počet stejný * nebo větší jako parametr ‹bytes›, do ‹output› budou zapsána jen * písmena (kódové body), která se do tohoto limitu vejdou. Výstupní * řetězec bude vždy ukončen nulou (parametr ‹bytes› tedy musí být * alespoň 1). */ int acrostic( const uint8_t *input, int in_count, uint32_t *output, int out_count ) { struct decode_state st = { 0 }; bool first = true; int out_idx = 0; for ( int in_idx = 0; in_idx < in_count; ++in_idx ) switch ( decode_byte( input[ in_idx ], &st ) ) { case 0: if ( first ) { if ( out_idx < out_count ) output[ out_idx ] = st.point; ++ out_idx; } first = st.point == '\n'; break; case 1: break; case 2: return -2; } return st.index == 0 ? out_idx : -2; } int main() { uint8_t lines[] = { 0x01, 0x7e, 0x00, 0x6c, // žl 0x00, 0x75, 0x01, 0x65, // uť 0x00, 0x6f, 0x00, 0x75, // ou 0x01, 0x0d, 0x00, 0x6b, // čk 0x00, 0xfd, 0x00, 0x0a, // ý\n 0x00, 0x6b, 0x01, 0x6f, // ků 0x01, 0x48, 0x00, 0x0a, // ň\n 0xd8, 0x01, 0xdc, 0x37 // 𐐷 }; uint32_t out[ 3 ] = { 0 }; assert( acrostic( lines, sizeof( lines ), out, 2 ) == 3 ); assert( out[ 0 ] == 0x017e ); assert( out[ 1 ] == 'k' ); assert( out[ 2 ] == 0 ); assert( acrostic( lines, sizeof( lines ), out, 3 ) == 3 ); assert( out[ 2 ] == 0x10437 ); assert( acrostic( lines, sizeof( lines ) - 4, out, 3 ) == 2 ); assert( acrostic( lines, sizeof( lines ) - 1, out, 3 ) == -2 ); lines[ sizeof( lines ) - 2 ] = 0; assert( acrostic( lines, sizeof( lines ), out, 3 ) == -2 ); return 0; } /* ¹ V „reálném světě“ by takové zjednodušení nebylo přípustné. * Skutečné aplikace využívají pro práci s textem rozsáhlé * knihovny, a/nebo jsou naprogramované v jazyce, který má pro * práci s textem zabudované prostředky. */