Перейти к основному содержимому

Строки и форматирование

Эти теги придают форму строке как шаг вычисления. Они живут внутри <compute>, рядом с <gen> в <sequence> и читают другие последовательности через <field name="…"/> — те же имена, что и в ${{…}}.

Большинство из них (mask, case, slice, replace, trim, group) доступны ещё двумя способами — как атрибут <gen> (mask= / case=) и как фильтр в интерполяции (${{X | mask:…}}). Руководство Маски и регистр показывает эти способы с готовым выводом. Берите тег compute, когда форматирование — звено вычисления: например, наложить маску на число и затем поднять регистр, прежде чем допишется контрольная цифра. <concat>, <str> и <pad> существуют только как теги compute.

примечание

Примеры вывода на этой странице иллюстративны — точные значения могут отличаться от версии к версии ядра. Важна форма каждого преобразования. В примерах стоит order="sequential", поэтому входные значения стабильны и повторяются по порядку.

Сборка и регистр

<concat> — склеить части в строку

Принимает любое число значений → отдаёт строку. Число по дороге становится своими цифрами.

<concat> приводит каждого ребёнка к строке и склеивает их по порядку, без разделителя. В отличие от <add>, это строковая операция: <concat> из "12" и "3" даёт "123", а не 15. Целое пишется своими десятичными цифрами; ребёнка-список сначала нужно превратить в строку через <join>.

Когда применять: когда вы собираете идентификатор из кусков — префикс, поле и вычисленная контрольная цифра.

<sequence name="Num"><gen type="number" value="100..999"/></sequence>
<sequence name="Year"><gen type="number" value="20..24"/></sequence>
<sequence name="Code">
<compute><result>
<concat><str v="ЗАК-"/><field name="Year"/><str v="-"/><field name="Num"/></concat>
</result></compute>
</sequence>
...
<data>${{Code}}</data>
./run order-code.tdc
ЗАК-24-692
ЗАК-21-695
ЗАК-23-979
ЗАК-24-326
ЗАК-24-587

Встроенного разделителя нет — дефисы это отдельные дети <str v="-"/>. Дописать вычисленную контрольную цифру — тот же приём: назовите остаток через <let>, затем приклейте его (целое само станет текстом).

<sequence name="Base"><gen type="number" value="1000..9999"/></sequence>
<sequence name="Full">
<compute>
<let name="c"><mod><to_number><field name="Base"/></to_number><int v="7"/></mod></let>
<result><concat><field name="Base"/><str v="-"/><var name="c"/></concat></result>
</compute>
</sequence>
...
<data>${{Full}}</data>
./run check-digit.tdc
5962-5
4783-2
6257-6
2280-5
6591-4

<str> — строковый литерал

Принимает ничего; текст живёт в v=отдаёт строку.

<str v="…"/> — это строковый литерал; текст пишется в атрибуте v (в TDC теги не могут держать текст между <тег> и </тег>).

Когда применять: когда нужен фиксированный префикс, разделитель внутри <concat> или символы дополнения — например, <str v=" "/>, который подставляет лишние пробелы в примере с <trim> ниже.

<upper> / <lower> / <capitalize> / <title> — регистр

Принимает одну строку → отдаёт строку той же длины.

Четыре преобразования регистра, каждое берёт одного ребёнка-строку:

ТегЧто делает
<upper>ВЕСЬ ВЕРХНИЙ регистр
<lower>весь нижний регистр
<capitalize>только первая буква заглавная, остальное как есть
<title>первая буква каждого слова заглавная, остальное как есть

Когда применять: когда данные приходят в «пёстром» регистре (разные источники, импорт) и нужен единый вид. Одна и та же строка через все четыре:

<sequence name="W"><gen type="text" value="iPhone ЧЕХОЛ,мария ИВАНОВА,АННА фон лее" order="sequential"/></sequence>
<sequence name="U"><compute><result><upper><field name="W"/></upper></result></compute></sequence>
<sequence name="L"><compute><result><lower><field name="W"/></lower></result></compute></sequence>
<sequence name="C"><compute><result><capitalize><field name="W"/></capitalize></result></compute></sequence>
<sequence name="T"><compute><result><title><field name="W"/></title></result></compute></sequence>
...
<data>${{W}} -> upper=${{U}} | lower=${{L}} | capitalize=${{C}} | title=${{T}}</data>
./run case.tdc
iPhone ЧЕХОЛ  ->  upper=IPHONE ЧЕХОЛ | lower=iphone чехол | capitalize=IPhone ЧЕХОЛ | title=IPhone ЧЕХОЛ
мария ИВАНОВА ->  upper=МАРИЯ ИВАНОВА | lower=мария иванова | capitalize=Мария ИВАНОВА | title=Мария ИВАНОВА
АННА фон лее  ->  upper=АННА ФОН ЛЕЕ | lower=анна фон лее | capitalize=АННА фон лее | title=АННА Фон Лее

capitalize трогает только самый первый символ (АННА фон лее осталась почти как есть — её первая А уже заглавная), а title поднимает первую букву каждого слова (фонФон, лееЛее). upper / lower меняют всё.

Перестройка

Эти пять тегов переставляют символы строки. Все они также доступны как фильтры ${{X | …}} (см. Маски и регистр); показанная здесь форма тега compute нужна, когда перестройка — звено вычисления.

<mask> — разбить и переставить по шаблону

Принимает одну строку плюс pattern=отдаёт строку. Ничего не выдумывается: каждый символ результата либо пришёл со входа, либо записан литералом в шаблоне.

<mask pattern="…"> пересобирает строку по позиционному шаблону: он идёт слева направо, каждый слот «съедает» кусок входа, а всё остальное печатается как литерал.

СлотЧто берёт из входа
xодин символ
wодно слово (буквы до пробела) и проглатывает один пробел
*весь оставшийся вход
\экранирует следующий символ (\x → буквальный x)
прочеелитерал: дефис, точка, пробел, скобки — печатается как есть

pattern — единственный (обязательный) атрибут. Маска снисходительна и никогда не падает: если x / w выходят за конец входа, они печатают пустоту, а любой оставшийся хвост отбрасывается, если его не подберёт *.

Когда применять: когда значение приходит слитной строкой и нужны группы и разделители — СНИЛС, номер карты, телефон. Одни и те же цифры под одним шаблоном:

<sequence name="Raw"><gen type="text" value="37898432363,88973572402,85213975315,26324315851" order="sequential"/></sequence>
<sequence name="Masked">
<compute><result><mask pattern="xxx-xxx-xxx xx"><field name="Raw"/></mask></result></compute>
</sequence>
...
<data>${{Raw}} -> ${{Masked}}</data>
./run mask-snils.tdc
37898432363  ->  378-984-323 63
88973572402  ->  889-735-724 02
85213975315  ->  852-139-753 15
26324315851  ->  263-243-158 51

Каждый x берёт один символ; - и пробел — литералы, шаблон читается как xxx-xxx-xxx xx.

Слот w — работать по словам. w берёт одно слово и проглатывает единственный пробел за ним, поэтому лишнего отступа не остаётся:

<sequence name="Full"><gen type="text" value="ivan sergeevich petrov,petr ivanovich smirnov,anna sergeevna orlova" order="sequential"/></sequence>
<sequence name="First">
<compute><result><mask pattern="w: *"><field name="Full"/></mask></result></compute>
</sequence>
...
<data>${{Full}} -> ${{First}}</data>
./run mask-word.tdc
ivan sergeevich petrov  ->  ivan: sergeevich petrov
petr ivanovich smirnov  ->  petr: ivanovich smirnov
anna sergeevna orlova   ->  anna: sergeevna orlova

Первый w берёт ivan и съедает его хвостовой пробел, печатается литерал : , а * подбирает остаток. Пробела перед : нет — его проглотил w.

Экранирование — буквальный w. Чтобы напечатать символ-слот как есть, экранируйте его через \:

<sequence name="Num"><gen type="text" value="1234,5678,9012" order="sequential"/></sequence>
<sequence name="Tagged">
<compute><result><mask pattern="\w-xxxx"><field name="Num"/></mask></result></compute>
</sequence>
...
<data>${{Num}} -> ${{Tagged}}</data>
./run mask-escape.tdc
1234  ->  w-1234
5678  ->  w-5678
9012  ->  w-9012

\w — это буквальная буква w (не слот), затем литерал -, затем четыре x берут четыре символа.

<slice> — подстрока по индексам

Принимает одну строку плюс from= и необязательный to=отдаёт строку. Счёт с 0, отрицательный from считает от конца, а диапазон за пределом даёт пустую строку, а не ошибку.

<slice from="…" to="…"> вырезает подстроку на полуоткрытом диапазоне [from, to): символ на позиции from входит, символ на позиции to — нет, поэтому длина равна to − from. Индексы с нуля и считаются в кодовых точках (Unicode режется по буквам, а не по байтам).

АтрибутОбязателенЗадаёт
fromдаиндекс первого символа (с нуля), включается
toнетиндекс сразу за последним символом; опущен → до конца

Когда применять: когда нужна фиксированная часть значения — год или месяц даты, префикс кода, последние символы идентификатора.

<sequence name="D"><gen type="text" value="2020-05-14,2022-11-03,2020-01-30,2021-07-19" order="sequential"/></sequence>
<sequence name="Year"><compute><result><slice from="0" to="4"><field name="D"/></slice></result></compute></sequence>
<sequence name="Month"><compute><result><slice from="5" to="7"><field name="D"/></slice></result></compute></sequence>
<sequence name="Tail"><compute><result><slice from="5"><field name="D"/></slice></result></compute></sequence>
...
<data>${{D}} -> year=${{Year}} | month=${{Month}} | tail=${{Tail}}</data>
./run slice.tdc
2020-05-14  ->  year=2020 | month=05 | tail=05-14
2022-11-03  ->  year=2022 | month=11 | tail=11-03
2020-01-30  ->  year=2020 | month=01 | tail=01-30
2021-07-19  ->  year=2021 | month=07 | tail=07-19

from=0 to=4 берёт символы 0–3 (год); from=5 to=7 берёт 5–6 (месяц — дефис на индексе 4 не входит); from=5 без to идёт от индекса 5 до конца.

<replace> — заменить все вхождения

Принимает одну строку плюс from= и to=отдаёт строку. from= сопоставляется буквально, а не как регулярное выражение, и заменяются все вхождения.

<replace from="…" to="…"> заменяет все вхождения буквальной подстроки from на to. Оба — обычные строки, а не регулярные выражения: что написали, то и ищется дословно. Если from пуста или не найдена, строка возвращается без изменений.

АтрибутОбязателенЗадаёт
fromдачто искать (литерал)
toдана что заменить

Когда применять: когда нужно поменять разделитель (дефис → слэш в дате) или убрать символ целиком (to="" удаляет каждое совпадение):

<sequence name="D"><gen type="text" value="2020-05-14,2022-11-03,2020-01-30,2021-07-19" order="sequential"/></sequence>
<sequence name="Slashed"><compute><result><replace from="-" to="/"><field name="D"/></replace></result></compute></sequence>
<sequence name="Bare"><compute><result><replace from="-" to=""><field name="D"/></replace></result></compute></sequence>
...
<data>${{D}} -> slash=${{Slashed}} | bare=${{Bare}}</data>
./run replace.tdc
2020-05-14  ->  slash=2020/05/14 | bare=20200514
2022-11-03  ->  slash=2022/11/03 | bare=20221103
2020-01-30  ->  slash=2020/01/30 | bare=20200130
2021-07-19  ->  slash=2021/07/19 | bare=20210719

Заменяются оба дефиса, а не только первый. Чтобы заменять по позиции, а не по подстроке, используйте <mask>.

<trim> — убрать крайние пробелы

Принимает одну строку → отдаёт строку. Трогаются только края; пробелы внутри остаются.

<trim> убирает пробелы с обоих краёв строки. Внутренние пробелы не трогает. Атрибутов не имеет.

Когда применять: когда значения из файла или CSV несут лишние пробелы по краям. Здесь пробелы добавлены нарочно (имитируя «грязные» исходные данные), а скобки делают их видимыми:

<sequence name="City"><gen type="text" value="Moscow,Berlin,Paris" order="sequential"/></sequence>
<sequence name="Padded">
<compute><result><concat><str v=" "/><field name="City"/><str v=" "/></concat></result></compute>
</sequence>
<sequence name="Clean">
<compute><result><trim><field name="Padded"/></trim></result></compute>
</sequence>
...
<data>[${{Padded}}] -> [${{Clean}}]</data>
./run trim.tdc
[  Moscow   ]  ->  [Moscow]
[  Berlin   ]  ->  [Berlin]
[  Paris   ]  ->  [Paris]

<group> — сгруппировать символы справа

Принимает одну строку плюс size= и sep=отдаёт строку. Группировка идёт справа налево, поэтому короткая группа оказывается слева — как пишут деньги.

<group size="…" sep="…"> разбивает строку на группы по size символов и вставляет sep между ними. Группировка идёт справа налево, поэтому короткая (неполная) группа оказывается слева, ровно как разделитель разрядов тысяч. Строка короче одной группы возвращается без изменений (разделитель не добавляется).

АтрибутОбязателенПо умолчаниюЗадаёт
sizeнет3размер группы (в символах)
sepнетпробел " "разделитель между группами

Когда применять: когда длинное число нечитаемо — разряды тысяч, блоки карты, длинные коды:

<sequence name="N"><gen type="text" value="1234567,42,1000000,89150000" order="sequential"/></sequence>
<sequence name="G3"><compute><result><group size="3"><field name="N"/></group></result></compute></sequence>
<sequence name="G3d"><compute><result><group size="3" sep="-"><field name="N"/></group></result></compute></sequence>
<sequence name="G4"><compute><result><group size="4"><field name="N"/></group></result></compute></sequence>
...
<data>${{N}} -> group3=${{G3}} | group3,-=${{G3d}} | group4=${{G4}}</data>
./run group.tdc
1234567   ->  group3=1 234 567 | group3,-=1-234-567 | group4=123 4567
42        ->  group3=42 | group3,-=42 | group4=42
1000000   ->  group3=1 000 000 | group3,-=1-000-000 | group4=100 0000
89150000  ->  group3=89 150 000 | group3,-=89-150-000 | group4=8915 0000

Поскольку группировка идёт справа, 1234567 разбивается как 1 234 567 — остаток 1 остаётся слева. group size="3" даёт разряды тысяч (разделитель по умолчанию — пробел), sep="-" задаёт свой разделитель, size="4" читается как блоки карты. Короткое 42 меньше одной группы, поэтому возвращается как есть. Чтобы группировать слева или по позиции, используйте <mask>.

<pad> — дополнить слева до фиксированной ширины

Принимает одно значение плюс width= и fill=отдаёт строку. width — это минимум: значение такой длины или длиннее проходит нетронутым, обрезки не бывает.

<pad width="…" fill="…"> приводит ребёнка к строке и дописывает fill слева, пока строка не достигнет width. Ведёт себя как padStart: если строка уже не короче width, она возвращается без изменений — обрезки нет.

АтрибутПо умолчаниюЗадаёт
widthцелевая ширина строки
fill"0"символ, дописываемый слева

Когда применять: когда идентификатору нужна фиксированная ширина — контрольный номер, код артикула, контрольная цифра, которая должна занимать два места (09). Ведущие нули до ширины 6:

<sequence name="Id"><gen type="number" value="1..9999"/></sequence>
<sequence name="Padded"><compute><result><pad width="6"><field name="Id"/></pad></result></compute></sequence>
...
<data>${{Id}} -> ${{Padded}}</data>
./run pad.tdc
1401  ->  001401
3593  ->  003593
7646  ->  007646
6755  ->  006755
4701  ->  004701

fill может быть любым символом, а значение длиннее width не трогается:

<sequence name="P1"><compute><result><pad width="6"><str v="42"/></pad></result></compute></sequence>
<sequence name="P2"><compute><result><pad width="6" fill="*"><str v="42"/></pad></result></compute></sequence>
<sequence name="P3"><compute><result><pad width="4"><str v="1234567"/></pad></result></compute></sequence>
...
<data>zero=${{P1}} | fill*=${{P2}} | over=${{P3}}</data>
./run pad-variants.tdc
zero=000042 | fill*=****42 | over=1234567

"42" дополняется до ширины 6 нулями или звёздочками; "1234567" уже длиннее 4, поэтому проходит насквозь — без обрезки. В отличие от mask / case / slice / group, у <pad> нет формы фильтра и нет атрибута <gen> — он работает только как тег compute.

Одно поведение — три способа

Форматирование доступно как тег compute, атрибут <gen> (mask= / case=) и фильтр в интерполяции (${{X | mask:…}}) — результат одинаковый. Руководство Маски и регистр показывает каждый способ с настоящим выводом. Где какие теги живут:

ОперацияФильтр ${{X|…}}Атрибут <gen>Тег <compute>
регистр (uppertitle)даcase=<upper>
maskдаmask=<mask>
slice / replace / trim / groupда<slice>
concat / str / padэта страница (только compute)

Берите тег compute, когда форматирование — звено вычисления; берите атрибут или фильтр для простого значения.

См. также

  • Маски и регистр — те же операции, что и фильтры ${{X|…}} и атрибуты <gen>, с готовым выводом.
  • Обзор compute — как <compute>, <let> и <field> работают вместе.
  • Арифметика<to_number> / <encode>, чтобы перейти от текста к числам.
  • Списки и перебор<join>, чтобы превратить список в строку перед <concat>.