Zkouška C++ 29. 1. 2026
Autorem zadání je Petr Škoda. Čas měl být asi 3.5 hodin, ale prodlužovalo se o asi 25 minut. Studenti se shodují, že to bylo složité, obzvláště vzhledem k času. K dispozici byl custom AI chat, který ale cenzuroval delší kusy kódu a tak třetinu času nefungoval.
Cílem je implementovat procesor úryvků (snippetů). Program přijímá dva vstupní soubory – jeden s daty a druhý s úryvkem. Program následně dosadí hodnoty z datového souboru do souboru úryvku a vypíše výsledný text na standardní výstup.
Například uvažujme následující datový soubor,
person.name=Max person.age=20 person.vegetarian=1
a jednoduchý soubor úryvku
Name: {{ person.name }}
Age: {{ person.age }}
Food preferences: {{ #if person.vegetarian }} vegetarian {{ /if }}
Jak je vidět, v souboru úryvku se hodně opakuje stejný prefix; můžeme jej tedy zjednodušit.
{{ #with person }}Name: {{ name }}
Age: {{ age }}
Food preferences: {{ #if vegetarian }} vegetarian {{ /if }}{{ /with }}
Šablona obsahuje tzv. řídicí značky např. {{ person.name }} nebo {{ #with person }}. Program musí:
načíst datový soubor do strukturované reprezentace,
načíst a validovat soubor úryvku,
vypsat soubor úryvku s vyhodnocenými řídicími značkami.
Následující sekce podrobně vysvětlují veškerou požadovanou funkcionalitu.
Rozhraní příkazové řádky
Program očekává dva argumenty příkazové řádky (bez názvu programu). Pokud počet argumentů není přesně dva, program vypíše Invalid number of arguments a skončí s návratovým kódem 1.
První argument je cesta k datovému souboru. Druhý argument je cesta k souboru úryvku. Jakékoliv selhání při otevírání některého ze souborů pro čtení způsobí, že program vypíše Can not read input files a skončí s návratovým kódem 1.
Pokud je datový soubor neplatný, program vypíše Invalid data file a skončí s návratovým kódem 1. Datový soubor musí být validován ještě před otevřením souboru úryvku.
Pokud je soubor úryvku neplatný, program vypíše Invalid snippet file a skončí s návratovým kódem 1. Částečný výstup není povolen pro neplatné soubory úryvku. Jinými slovy, program musí soubor nejprve kompletně validovat a teprve poté ji zpracovat.
Chybová hlášení musí být vypsána na std::cout.
Datový soubor
Datový soubor je řádkově orientovaný a celý se vejde do operační paměti. Řádky začínající znakem ; mají být ignorovány, stejně jako prázdné řádky. Ostatní řádky musí obsahovat <key> a <value> oddělené znakem =, kde <key> je case-sensitive. Pokud datový soubor obsahuje více hodnot pro stejný klíč, je soubor neplatný. Pokud soubor obsahuje neplatný klíč, je neplatný celý soubor.
Jednoduchý datový soubor:
name=Alistar age=30
<key> může obsahovat jeden nebo více podklíčů oddělených tečkou .. Podklíč je neprázdný řetězec a skládá se ze znaků [A–Za–z0–9], můžete použít funkci std::isalnum. <value> je vše za znakem = až do konce řádku, bez znaku konce řádku (\n). <value> může být prázdný řetězec.
Příklad složitějšího datového souboru:
; Alistar student.006.name=Alistar student.007.grades.NPRG030.grade=A ; Max student.007.name=Max student.007.grades.NPRG041.grade=C ; The value for the next line is an empty string. teacher.606.name= ; The next line contains a value with white spaces. teacher.700.name=Dr. Lada
Příklad neplatného datového souboru:
; Space before `=`. name =Igor ; Invalid character in the key. invalidCharacterInKey_=12 ; Key can not end with '.'. trainingDor.=12 ; Key can not be empty. =12 ; Sub-key can not be empty. doubleDot..=12 ; Space in key is not allowed this. isNotValid=12
Soubor úryvku a řídicí značky
Řídicí značka je otevřena pomocí {{ a uzavřena pomocí }}. Veškerý text mimo řídicí značky je zapsán na standardní výstup. Escapování řídicích značek není podporováno, tzn. není možné vypsat {{ nebo }}.
Program musí podporovat následující řídicí značky:
{{<relative-key>}}{{#with <relative-key>}},{{/with}}- párová značka{{#if <relative-key>}},{{/if}}- párová značka{{#foreach <relative-key>}},{{/foreach}}- párová značka
Další informace ke každé řídicí značce jsou uvedeny v následujících sekcích. Značky mohou být vnořené. <relative-key> musí splňovat stejná kritéria jako <key> z části o datovém souboru.
Znaky bílého místa ( , \t, \r a \n) za {{ a před }} je třeba ignorovat. Vícenásobné mezery po #if, #with a #foreach jsou také ignorovány.
Následující zápisy jsou ekvivalentní:
{{name}}
{{ name}}
{{ name }}
{{
name
}}
Následující zápisy jsou ekvivalentní:
{{#if name}}
{{ #if name }}
{{
#if
name
}}
Párové řídicí značky umožňují vnoření, například:
{{ #with person }} {{ #if render }} {{ /if }} {{ /with }}
Počet úrovní vnoření není omezen. Můžete předpokládat, že soubor úryvku je neplatný, pokud:
existují nespárované
{{nebo}},je použita neznámá řídicí značka,
párové řídicí značky jsou špatně spárované nebo nesprávně vnořené.
Příklad neplatné šablony se špatně vnořenými párovými značkami:
{{ #with person }} {{ #if render }} {{ /with }} {{ /if }}
Můžete předpokládat, že se každá dvojice párových značek a její obsah vejde do hlavní paměti. Celý soubor nebo dokonce jeden řádek se však do paměti vejít nemusí.
Odkazování na datový soubor
Řídicí značky mohou odkazovat na položku datového souboru (dvojici klíč, hodnota). Aby bylo možné získat klíč s klíčem = <key>, musí být <relative-key> zkombinován s <base-key>.
<base-key> je na začátku programu prázdný a může být měněn pomocí řídicích značek (viz následující sekce).
Odvození hodnoty <key>
<key>=<relative-key>pokud je<base-key>prázdný<key>=<base-key>.<relative-key>
Řídicí značky
Tato sekce popisuje všechny řídicí značky, které musí program podporovat. Pro každou z nich se hodnota <key> počítá podle pravidel uvedených v části Odkazování na datový soubor.
Výpis hodnoty
{{ <relative-key> }}
Značka se vyhodnotí na hodnotu položky z datového souboru pod klíčem <key>. Pokud taková položka neexistuje, značka se vyhodnotí jako prázdný řetězec.
Změna <base-key>
{{ #with <relative-key> }}...{{ /with }}
Tato značka změní <base-key> na <key> (získaný z <relative-key>) pro vnořený obsah (...). Změna <base-key> platí pouze v rámci vnořeného obsahu.
Například následující řádky produkují stejný výstup:
{{ person.name }}
{{ #with person }}{{ name }}{{ /with }}
Podmíněné vyhodnocení
{{ #if <relative-key> }}...{{ /if }}
Vnořený obsah je zpracován pouze tehdy, pokud:
existuje položka v datovém souboru s klíčem
<key>(odvozená z<relative-key>),a hodnota této položky není prázdná.
Například pro následující datový soubor
person.good=1 person.evil=
a úryvek
-{{ #if person.good }}good{{ /if }}
-{{ #if person.neutral }}neutral{{ /if }}
-{{ #if person.evil }}evil{{ /if }}
je výstup
-good - -
Smyčky
{{ #foreach <relative-key> }}...{{ /foreach }}
Tato značka slouží k iterování přes podklíče. Uvažme následující datový soubor
person.b.name=1 person.a.name=0 person.c.name=2
a úryvek
{{ #foreach person }}{{ name }},{{ /foreach }}
výstupem bude
0,1,2,
Značka je vyhodnocena pro každý podklíč S klíče <key> (získaného z <relative-key>) a to v abecedním pořadí. Pokud <key> nemá žádné podklíče, značka nevygeneruje žádný výstup.
Pro každý podklíč S se nastaví <base-key> pro vnořený obsah (...) na <key>.S. Změna <base-key> platí pouze v rámci vnořeného obsahu.
Ve výše uvedeném příkladě je tedy <base-key> nastaven nejprve na person.a, pak person.b a nakonec na person.c. Vnořený obsah {{ name }}, je tak vyhodnocen celkem třikrát.
Příklad - Test 17
Datový soubor:
student.0.name=Max student.0.cpp.points=20 student.0.cpp.test=30 student.0.cpp.grade= ; student.1.name=Ailish student.1.cpp.points=20 student.1.cpp.test=40 student.1.cpp.grade=A ; student.2.name=Jorg student.2.cpp.points=15 ; student.3.name=Chuck
Soubor úryvku:
Student, Tutorials, Test, Grade, Passed
{{ #foreach student }}{{ name }},{{ #with cpp}} {{ points }}, {{ test }}, {{ grade }},{{ #if grade }} passed{{ /if}}{{ /with }}
{{ /foreach }}
Očekávaný výstup:
Student, Tutorials, Test, Grade, Passed Max, 20, 30, , Ailish, 20, 40, A, passed Jorg, 15, , , Chuck, , , ,