6.2. Příkaz STRING Formát: --- - - - - - ||| položka | | |celé-číslo | | STRING <<< alf.literál > |WITH POINTER < > | ||| fig.konst. | | |celočís.položka| | ||| FILLER | - - - - --- - - - - | | LENGTH celé-číslo | > ... | LENGTH celočíselná-položka | | - - - - - - - - | | SIZE | | | | DELIMITED BY < položka > | > ... | | alfanum.literál | | | | | figur.konstanta | | | - - - - - - - - - - - | INTO | | | celé-číslo | | < > položka | WITH POINTER < > | | TO | | | celočíselná-položka | | - - - - - - - - | LENGTH celé-číslo | | LENGTH celočíselná-položka | - - [ ON OVERFLOW příkaz ... ] Funkce: Příkaz STRING přesunuje několik vysílajících údajů bezprostředně za sebe do jedné příjmové položky. Vysílajícími údaji jsou ty údaje, které jsou ve výše uvedeném formátu uvedeny bezprostředně za slovem STRING (položka, alfanumerický literál, figurativní konstanta, FILLER). Položka může mít libovolný typ (zpracovává se však vždy alfanumericky) a pevnou nebo proměnnou délku nejvýše 32767 bytů. Figurativní konstanta se zpracovává jako alfanumerický literál o délce 1 znak. Slovo FILLER způsobí vynechání jednoho bytu příjmové položky, jehož obsah tedy zůstane beze změny. (Slovo FILLER je výhodné používat ve spojení s klauzulí LENGTH, což způsobí vynechání několika bytů příjmové položky.) Za každým z vysílajících údajů (s výjimkou FILLER) může být uvedena klauzule POINTER s tímto významem: a) Není-li za vysílajícím údajem uvedena klauzule POINTER, zpracovává se tento vysílající údaj počínaje svým prvním bytem, tzn. od začátku. b) Je-li za vysílajícím údajem uvedena klauzule POINTER, určuje hodnota argumentu v ní uvedeného (celého čísla resp. celočíselné položky) pořadové číslo toho bytu vysílajícího údaje, od něhož se vysílající údaj začne zpracovávat; tedy až od tohoto bytu se začíná hledat oddělovač a tímto bytem též začíná ten úsek vysílajícího údaje, který bude přesunován do příjmové položky. Předešlé byty vysílajícího údaje se nevyužívají. Je-li za slovem POINTER uvedeno celé číslo, musí být kladné. Je tedy ekvivalentní neuvést klauzuli POINTER vůbec anebo ji uvést ve tvaru "POINTER 1". Poznámka: Za normálních okolností by měla být hodnota argumentu uvedeného za slovem POINTER kladná a nejvýše rovná délce vysílajícího údaje, není to však vyžadováno ani kontrolováno. V případě uvedení položky s nekladnou hodnotou se bude přesunovat úsek paměti začínající příslušný počet bytů před začátkem vysílajícího údaje. V případě uvedení celého čísla resp. položky s hodnotou převyšující délku vysílajícího údaje se vysílající údaj buďto ignoruje (nemá-li uvedenu klauzuli LENGTH) anebo se chápe jako FILLER (má-li uvedenu klauzuli LENGTH). Za každým z vysílajících údajů (včetně FILLER) může být uvedena ještě klauzule LENGTH s tímto významem: a) Není-li za vysílajícím údajem uvedena klauzule LENGTH, zabere se pro tento vysílající údaj v příjmové položce tolik bytů, kolik se jich z něj přesouvá, to jest - při DELIMITED SIZE: plná délka vysílajícího údaje resp. délka celého zbytku vysílajícího údaje začínajícího bytem určeným klauzulí POINTER; - při DELIMITED jiném než SIZE: délka úseku vysílajícího údaje před prvním oddělovačem, a to od začátku resp. od bytu určeného klauzulí POINTER. b) Je-li za vysílajícím údajem uvedena klauzule LENGTH, zabere se pro tento vysílající údaj v příjmové položce tolik bytů, jakou hodnotu má argument uvedený v klauzuli LENGTH. Je-li tato hodnota větší než počet bytů přesunovaných z vysílajícího údaje (viz bod a), uloží se přesunovaná část vysílajícího údaje do příjmové položky opakovaně (jako při ALL). Je-li tato hodnota menší, přesune se z vysílajícího údaje jen tento zmenšený počet bytů. Jak je vidět z formátu, lze v případě celého čísla v klauzuli LENGTH vynechat slovo LENGTH a uvést tedy pouze toto celé číslo. Celé číslo uvedené v klauzuli LENGTH smí být rovno nule; v tom případě se tento vysílající údaj ignoruje. Použití klauzule LENGTH je výhodné především v těchto případech: a) U figurativní konstanty (obzvláště u SPACE) nebo u jednoznakového alfanumerického literálu, kde umožňuje vyplnit tímto znakem předepsaný počet bytů příjmové položky. (Např. SPACE 35 znamená zařazení 35 mezer do příjmové položky, SPACE LENGTH I znamená zařazení I mezer, '*' 50 znamená zařazení 50 hvězdiček atd.; 'HURA' 100 by znamenalo zařazení 25-krát opakovaného slova HURA (nikoliv 100-krát; uvedená hodnota znamená délku a nikoliv počet opakování).) b) U slova FILLER, kde umožňuje vynechat (tj. ponechat beze změny) předepsaný počet bytů příjmové položky. (Např. FILLER 30 znamená vynechání 30 bytů, FILLER LENGTH K znamená vynechání K bytů příjmové položky.) c) Chceme-li vzít z vysílající položky jen její začátek anebo obecněji jistou část, kterou nehodláme vyčlenit pomocí popisů. (Tak např. 1.-10. byte položky ALFA přesuneme pomocí "ALFA 10", 17.-25. byte položky ALFA přesuneme pomocí "ALFA POINTER 17 9", I-tý byte položky ALFA přesuneme pomocí "ALFA POINTER I 1", prvních K bytů položky ALFA přesuneme pomocí "ALFA LENGTH K", I-tý až (I+K-1)-tý byte položky ALFA přesuneme pomocí "ALFA POINTER I LENGTH K".) Za jedním nebo za několika vysílajícími údaji (opatřenými případně ještě klauzulemi POINTER a LENGTH) se uvádí klauzule DELIMITED, která se vztahuje ke všem předcházejícím vysílajícím údajům za předcházející klauzulí DELIMITED (pokud ještě žádná klauzule DELIMITED nebyla, pak od začátku příkazu STRING). Takto je začátek příkazu STRING před slovem INTO rozdělen na několik částí, z nichž každá obsahuje jeden nebo několik vysílajících údajů (opatřených případně ještě klauzulemi POINTER a LENGTH) následovaných jednou klauzulí DELIMITED, která se k nim všem vztahuje. (V praxi má ovšem příkaz STRING téměř vždy pouze jednu takovou část, tj. má pouze jednu klauzuli DELIMITED.) U poslední (resp. jediné) z těchto částí může být klauzule DELIMITED vynechána, přičemž vynechání je ekvivalentní s uvedením zápisu "DELIMITED SIZE". Budeme-li klauzule POINTER a LENGTH považovat za součást vysílajícího údaje resp. příjmové položky, můžeme zapsat formát příkazu STRING zjednodušeně takto: STRING {vysílající-údaj...[DELIMITED BY {SIZE|omezovač}]}... INTO příjmová-položka [ON OVERFLOW příkaz ...] Je-li potřebná pouze jedna klauzule DELIMITED, a to DELIMITED SIZE (a při praktickém použití příkazu STRING je tomu takto téměř vždy), lze slova DELIMITED SIZE vynechat. Protože v tomto případě i klauzule ON OVERFLOW nemá téměř žádný praktický význam, zní nejužívanější formát příkazu STRING takto: STRING vysílající-údaj ... INTO příjmová-položka Přitom ovšem u každého vysílajícího údaje i u příjmové položky mohou být použity klauzule POINTER a LENGTH. Je-li za slovem DELIMITED uvedeno slovo SIZE (anebo je-li klauzule DELIMITED vynechána), přesunují se všechny předcházející vysílající údaje patřící k této klauzuli DELIMITED do příjmové položky celé, v plné délce (resp. v plné délce zbytku údaje začínajícího bytem určeným klauzulí POINTER resp. v délce určené klauzulí LENGTH). Jinak stojí za slovem DELIMITED tzv. "omezovač". Může to být položka libovolného typu (zpracovává se však vždy alfanumericky) s pevnou nebo proměnnou délkou nejvýše 32767 bytů. Dále to může být alfanumerický literál nebo figurativní konstanta (která se zpracovává jako alfanumerický literál o délce 1 znak). V případě uvedení klauzule "DELIMITED omezovač" se každý z předcházejících vysílajících údajů patřících k této klauzuli DELIMITED prohledává (alfanumerickým srovnáním aplikovaným na každou pozici vysílajícího údaje), zda v jeho textu není obsažen zadaný omezovač. Je-li však vysílajícím údajem alfanumerický literál nebo figurativní konstanta a omezovačem je též alfanumerický literál nebo figurativní konstanta, hledání omezovače se neprovádí a tento vysílající údaj se přesunuje do příjmové položky celý, v plné délce (jako při DELIMITED SIZE). a) Je-li v textu vysílajícího údaje omezovač obsažen, přesune se do příjmové položky pouze začátek vysílajícího údaje končící posledním bytem před nalezeným omezovačem (resp. úsek začínající bytem určeným klauzulí POINTER a končící posledním bytem před nalezeným omezovačem). Omezovač sám se již nepřesunuje. Je-li v textu vysílajícího údaje obsaženo několik exemplářů omezovače, uvažuje se první z nich (zleva). Stojí-li omezovač hned na začátku vysílajícího údaje, nepřesunuje se z vysílajícího údaje nic (vysílající údaj se pak chová jako FILLER). b) Není-li v textu vysílajícího údaje omezovač obsažen, přesune se vysílající údaj do příjmové položky celý, v plné délce (jako při DELIMITED SIZE). Příjmovou položkou je položka uvedená za slovem INTO resp. TO (obě slova mají týž význam); smí mít libovolný typ i délku, zpracovává se však vždy alfanumericky. Za příjmovou položkou může být uvedena klauzule POINTER s tímto významem: a) Není-li za příjmovou položkou uvedena klauzule POINTER, začnou se příslušné části vysílajících údajů přesunovat do příjmové položky počínaje jejím prvním bytem, tj. od začátku příjmové položky. b) Je-li za příjmovou položkou uvedena klauzule POINTER s celočíselnou numerickou položkou, musí programátor před začátkem provádění příkazu STRING dosadit do této položky nějakou hodnotu. Je-li tato hodnota větší než délka příjmové položky, přičemž není uvedena klauzule LENGTH s kladnou hodnotou, nebude příkaz STRING vůbec prováděn. Je-li tato hodnota nejvýše rovna délce příjmové položky anebo je-li uvedena klauzule LENGTH s kladnou hodnotou, pak příkaz STRING prováděn bude, přičemž hodnota položky uvedené v klauzuli POINTER určuje pořadové číslo toho bytu příjmové položky, od kterého počínaje se začnou přesunovat příslušné části vysílajících údajů. Předcházející byty příjmové položky zůstanou beze změny. Navíc po ukončení provádění příkazu STRING bude do celočíselné položky uvedené za slovem POINTER dosazeno pořadové číslo toho bytu příjmové položky, který bezprostředně následuje za celým přesunutým textem. (Pokud se tedy jako poslední obsadil k-tý byte příjmové položky, bude do celočíselné položky uvedené za slovem POINTER dosazena hodnota k+1. Pokud se obsadila celá příjmová položka až do konce, bude do této celočíselné položky dosazena délka příjmové položky zvětšená o jednu.) Tato celočíselná položka musí mít v PICTURE dostatečný počet znaků 9, aby byla schopna pojmout tu hodnotu, která bude do ní ukládána. c) Je-li za příjmovou položkou uvedena klauzule POINTER s celým číslem, je účinek stejný, jako kdyby se jednalo o celočíselnou numerickou položku s takovou hodnotou. Rozdíl je pouze v tom, že uvedené celé číslo se samozřejmě během výpočtu nemění a po ukončení provádění příkazu STRING nedostává uživatel k dispozici pořadové číslo bytu příjmové položky bezprostředně následujícího za celým přesunutým textem. Poznámka: Za normálních okolností by měla být hodnota argumentu uvedeného za slovem POINTER kladná a nejvýše rovná délce příjmové položky, není to však vyžadováno ani kontrolováno. V případě uvedení položky s nekladnou hodnotou budou vysílající údaje přesunovány do úseku paměti začínajícího příslušný počet bytů před začátkem příjmové položky (dojde tedy k přemazání předcházejících položek). V případě uvedení celého čísla resp. položky s hodnotou převyšující délku příjmové položky se buďto příkaz STRING vůbec neprovádí (nemá-li příjmová položka uvedenu klauzuli LENGTH) anebo budou vysílající údaje přesunovány do úseku paměti začínajícího až někde za koncem příjmové položky (má-li příjmová položka uvedenu klauzuli LENGTH; dojde tedy k přemazání následujících položek). Dále může být za příjmovou položkou uvedena ještě klauzule LENGTH (v témže formátu jako u vysílajícího údaje) s tímto významem: a) Není-li za příjmovou položkou uvedena klauzule LENGTH, je k dispozici pro příjem přesunovaných částí vysílajících údajů celá příjmová položka (resp. celý její zbytek začínající bytem určeným klauzulí POINTER až do konce příjmové položky). b) Je-li za příjmovou položkou uvedena klauzule LENGTH, určuje hodnota argumentu v ní uvedeného "opravenou" délku příjmové položky (počínaje jejím prvním bytem resp. bytem určeným klauzulí POINTER), která je k dispozici pro příjem přesunovaných částí vysílajících údajů. Vysílající údaje (resp. jejich části) se přesunují do příjmové položky bez jakýchkoliv změn a úprav bezprostředně za sebe bez mezer a bez jakýchkoliv oddělovačů. (Chce-li uživatel zařadit nějaký oddělovač, musí jej uvést jako samostatný vysílající údaj.) Neprovádějí se žádné ediční úpravy numerických položek, takže v případě potřeby je musí uživatel před provedením příkazu STRING sám poslat do pracovních numerických editovaných položek a tyto pak uvést v příkazu STRING. Provádění příkazu STRING se ukončí, jakmile nastane některá z těchto situací: a) Příjmová položka je již zaplněna až do konce. V tom případě se další vysílající údaje již neuplatní. Z vysílajícího údaje, který se uplatnil jako poslední, se přitom vzalo jen tolik bytů (zleva), kolik chybělo do konce příjmové položky. b) Už se zpracovaly a přenesly všechny vysílající údaje. V tom případě zůstane zbytek příjmové položky beze změny. Je-li uvedena klauzule ON OVERFLOW, dojde k jejímu uplatnění v těchto případech: a) Délka té části příjmové položky, která je k dispozici pro příjem přesunovaných částí vysílajících údajů, je záporná. To nastává v těchto případech: - Příjmová položka má zápornou délku, klauzule POINTER a LENGTH pro ni nejsou uvedeny. - Za příjmovou položkou je uvedena klauzule LENGTH s celočíselnou numerickou položkou, jejíž hodnota je záporná. - Není uvedena klauzule LENGTH, avšak je uvedena klauzule POINTER s argumentem, jehož hodnota je větší než délka příjmové položky zvětšená o jedničku. Ve všech těchto případech se příkaz STRING vůbec neprovádí. b) Příjmová položka je již zaplněna až do konce a ještě zbývají nějaké neuplatněné vysílající údaje anebo v posledním vysílajícím údaji, který se uplatnil, musely být z přesunu vypuštěny poslední byty. Pokud k některému z těchto případů došlo, budou se provádět příkazy uvedené za ON OVERFLOW. Pokud k žádnému z těchto případů nedošlo, příkazy uvedené za ON OVERFLOW se přeskočí a jako další se bude provádět první příkaz za tečkou. Není-li klauzule ON OVERFLOW uvedena, bude se po provedení příkazu STRING provádět vždy ten příkaz, který za ním následuje. Poznámka: Pokud žádná z položek uvedených v příkazu STRING nemá proměnnou délku, v žádné z klauzulí POINTER nebo LENGTH není uvedena celočíselná položka, není uvedena klauzule DELIMITED s jinou volbou než SIZE a není uvedena klauzule ON OVERFLOW, nebude příkaz STRING realizován odskokem do podprogramu, nýbrž bude přeložen přímo do příkazů jazyka C (přesuny bytů a přesuny vícebytových struktur), takže použití příkazu STRING dá stejně rychlý výpočet, jako kdybychom přesuny rozepsali pomocí příkazů MOVE. Při nesplnění některé z těchto podmínek bude příkaz STRING realizován odskokem do podprogramu, čímž se výpočet samozřejmě mnohonásobně zpomalí. Proto by se měl programátor snažit - pokud je to ovšem v daném případě možné - zmíněné podmínky splnit. Příklad: 56.-63. byte položky ALFA můžeme přesunout na 23.-30. byte položky BETA bez popisování podřízených položek k ALFA a k BETA příkazem (jenž bude přeložen jediným příkazem jazyka C provádějícím přesun osmibytových struktur): STRING ALFA POINTER 56 8 INTO BETA POINTER 23 8. Příklad: Příkazem STRING lze velmi jednoduše naplnit logickou větu pro tiskárnu např. takto: STRING CISLO SPACE 5 JMENO SPACE 4 ADRESA SPACE 5 MZDA SPACE 54 INTO VETA POINTER 2. Příkaz bude přeložen do příkazů jazyka C provádějících přesun vícebytových struktur. Při této taktice položka VETA nemusí mít rozepisovánu žádnou podřízenou strukturu, nýbrž může mít popis pouze např. 01 VETA PIC X(12). Příklad: Do položky ALFA chceme na 14.-22. byte přesunout položku BETA (o délce 9 bytů), na 38.-44. byte přesunout literál 'SLEPICE' a na 86.-118. byte přesunout položku GAMA (o délce 33 bytů). To zařídíme jediným příkazem (který bude přeložen do tří příkazů jazyka C provádějících přesuny vícebytových struktur): STRING FILLER 13 BETA FILLER 15 'SLEPICE' FILLER 41 GAMA INTO ALFA. Ostatní byty položky ALFA zůstanou nezměněny. Místo prvního "FILLER 13" mohla též být v témže významu za příjmovou položkou ALFA uvedena klauzule "POINTER 14". Příklad: Chceme-li na I-tý byte položky ALFA umístit hvězdičku, můžeme tak učinit příkazem STRING '*' INTO ALFA POINTER I. V tomto případě však použití příkazu STRING je nevýhodné, neboť se realizuje odskokem do podprogramu. Místo toho je vhodné redefinovat položku ALFA pomocí pole s popisem např. "02 B PIC X OCCURS 1000." a provést příkaz "MOVE '*' TO B(I)". Naproti tomu příkaz STRING '*' INTO ALFA POINTER 50 by byl přeložen jediným příkazem jazyka C. Příklad: V 6-bytové položce A je uložen čas ve tvaru hhmmvv. Tento čas chceme přesunout do 8-bytové položky B a upravit jej do tvaru hh:mm:vv. Učiníme tak příkazem STRING A 2 ':' A pointer 3 2 ':' A POINTER 5 INTO B. Příklad: STRING H SPACE 25 I LENGTH J FILLER 8 K 1 INTO L. Je-li položka L dostatečně dlouhá, dá se do ní (počínaje jejím prvním bytem) celá položka H, 25 mezer, prvních J bytů položky I, dalších 8 bytů položky L zůstane beze změny a za ně se přesune první byte položky K. Příklad: 77 A PIC X(12). 77 B PIC XXX VALUE 'ABC'. 77 C PIC 999 VALUE 54. 77 I PIC 99. a) MOVE ZERO TO A. STRING B '123' C QUOTE DELIMITED SIZE INTO A. Položka A získá obsah ABC123054'00. b) STRING B '12' B '123456' C INTO A ON OVERFLOW DISPLAY A. Položka A získá obsah ABC12ABC1234. Znaky 56 a položka C se již neuplatní. Uplatní se klauzule ON OVERFLOW a provede se tedy příkaz DISPLAY. (Takovýto příkaz STRING je samozřejmě z praktického hlediska nesmyslný.) c) MOVE ALL '7' TO A. MOVE 4 TO I. STRING ZERO, B, 'EF' DELIMITED SIZE INTO A WITH POINTER I ON OVERFLOW DISPLAY A. MOVE ... Položka A získá obsah 7770ABCEF777, položka I získá obsah 10. Klauzule ON OVERFLOW se neuplatní, příkaz DISPLAY se tedy přeskočí a bude se provádět příkaz MOVE. Při hodnotě I > 7 by se však klauzule ON OVERFLOW uplatnila. d) MOVE ALL 'H' TO A. MOVE 5 TO I. STRING C '12345678' B INTO A POINTER I OVERFLOW ADD ... Položka A získá obsah HHHH05412345; znaky 678 a položka B se již neuplatní. Položka I získá hodnotu 13. Uplatní se klauzule OVERFLOW a bude se tedy provádět příkaz ADD. e) MOVE ZERO TO A. STRING B 'ABC' C 'C' DELIMITED 'C' INTO A. Položka A získá obsah ABABC054C000 (z B dostane AB, v 'ABC' a v 'C' se znak C nehledá, v položce C znak není). f) MOVE ALL '3' TO A. MOVE 3 TO I. STRING B DELIMITED BY 'B' '=' B DELIMITED SIZE C ZERO B DELIMITED ZERO INTO A POINTER I Položka A získá obsah 33A=ABC0ABC3, položka I získá hodnotu 12. g) STRING 'ABAABCDABC', C, '1234567ABCD' DELIMITED B INTO A ON OVERFLOW GO TO VEN. Položka A získá obsah ABA054123456, klauzule ON OVERFLOW se uplatní a provede se tedy příkaz GO. Příklad: 77 A PIC 9(18) COMP-3 VALUE 123456789. 77 D PIC S9(4) COMP-3 VALUE +5432. 77 E PIC X(6) VALUE 'AB#C#D'. 77 F PIC S9(3) COMP VALUE 8961. & hex. 0123 77 G PIC S9 COMP-3 VALUE 2. ... STRING D SPACES E HIGH-VALUES F QUOTE DELIMITED G INTO A. Položka G má obsah hexadecimálně 23, což odpovídá #. Položka A tedy získá obsah hex. 0543204142FF01277893. Příklad: V každé z položek B(1), B(2), ..., B(100) máme vyhledat první lomítko a počáteční úseky všech těchto položek před těmito prvními lomítky máme uložit za sebe do položky A, přičemž tyto úseky máme oddělit vždy jedním lomítkem. Zbytek položky A máme vymezerovat. MOVE 1 TO I P. CYKL. STRING B(I) '/' DELIMITED '/' INTO A POINTER P. IF I < 100 ADD 1 TO I GO TO CYKL. STRING SPACE 10000 INTO A POINTER P. První z obou příkazů STRING si sám posunuje ukazovátko P obsahující pořadové číslo toho bytu položky A, od něhož počínaje se má přesunovat další úsek.