Saltar al contenido principal

Strings y formato

Estas etiquetas le dan forma a un string como un paso dentro de un cálculo. Viven dentro de <compute>, junto a <gen> en una <sequence>, y leen otras secuencias con <field name="…"/> — los mismos nombres que usaría en ${{…}}.

La mayoría también están disponibles fuera de <compute>, aunque no por las mismas vías: las seis — mask, case, slice, replace, trim y group — funcionan como filtro de interpolación (${{X | mask:…}}), y dos de ellas, mask y case, además como atributo de <gen>. <gen slice="1,3"> se rechaza con TDC015. La guía Máscaras y mayúsculas muestra esas rutas con salidas resueltas. Eche mano de la etiqueta de compute cuando el formato sea un paso dentro de un cálculo — por ejemplo, enmascarar un número y después ponerlo en mayúsculas antes de agregarle un dígito verificador. <concat>, <str> y <pad> existen solo como etiquetas de compute.

nota

Las salidas de ejemplo de esta página son ilustrativas — los valores exactos pueden variar según la versión del núcleo. Lo que importa es la forma de cada transformación. Los ejemplos usan order="sequential" para que los valores de entrada se mantengan estables y se repitan en orden.

Construcción y mayúsculas

<concat> — pegar partes en un string

Toma cualquier cantidad de valores → da un string. Un número se vuelve sus dígitos en el camino.

<concat> convierte cada hijo a string y los une en orden y sin separador. A diferencia de <add>, esta es una operación de strings: <concat> de "12" y "3" da "123", no 15. Un hijo entero se escribe con sus dígitos decimales; un hijo de tipo lista primero hay que convertirlo en string con <join>.

Úselo cuando arme un identificador a partir de piezas: un prefijo, un campo y un dígito verificador calculado.

<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="ORD-"/><field name="Year"/><str v="-"/><field name="Num"/></concat>
</result></compute>
</sequence>
...
<data>${{Code}}</data>
./run order-code.tdc
ORD-24-692
ORD-21-695
ORD-23-979
ORD-24-326
ORD-24-587

No hay separador integrado — los guiones son sus propios hijos <str v="-"/>. Agregar un dígito verificador calculado es el mismo movimiento: nombre el residuo con <let> y luego péguelo (el entero se vuelve texto automáticamente).

<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="-"/><use name="c"/></concat></result>
</compute>
</sequence>
...
<data>${{Full}}</data>
./run check-digit.tdc
5962-5
4783-2
6257-6
2280-5
6591-4

<str> — un literal de string

Toma nada; el texto vive en v=da un string.

<str v="…"/> es un string literal; el texto va en el atributo v (en TDC, las etiquetas no pueden contener texto entre <tag> y </tag>).

Úselo cuando necesite un prefijo fijo, un separador dentro de <concat>, o caracteres de relleno — por ejemplo el <str v=" "/> que finge los espacios sobrantes en el ejemplo de <trim> de más abajo.

<upper> / <lower> / <capitalize> / <title> — mayúsculas y minúsculas

Toma un string → da un string. Normalmente del mismo largo, pero no siempre: straße en mayúsculas es STRASSE, siete caracteres a partir de seis, y se vuelve FI. No arme una tubería de ancho fijo suponiendo que la cuenta sobrevive.

Cuatro transformaciones de mayúsculas y minúsculas, cada una con un hijo de tipo string:

EtiquetaHace
<upper>TODO EN MAYÚSCULAS
<lower>todo en minúsculas
<capitalize>solo la primera letra en mayúscula, el resto tal cual
<title>la primera letra de cada palabra separada por espacios en mayúscula, el resto tal cual

Úselas cuando los datos lleguen con mayúsculas y minúsculas mezcladas (distintas fuentes, importaciones) y necesite una sola forma consistente. El mismo string a través de las cuatro:

<sequence name="W"><gen type="text" value="iPhone FUNDA,maría JIMÉNEZ,ANA de la torre" 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 FUNDA     ->  upper=IPHONE FUNDA | lower=iphone funda | capitalize=IPhone FUNDA | title=IPhone FUNDA
maría JIMÉNEZ    ->  upper=MARÍA JIMÉNEZ | lower=maría jiménez | capitalize=María JIMÉNEZ | title=María JIMÉNEZ
ANA de la torre  ->  upper=ANA DE LA TORRE | lower=ana de la torre | capitalize=ANA de la torre | title=ANA De La Torre

capitalize toca únicamente el primerísimo carácter (ANA de la torre se queda casi igual — su primera A ya está en mayúscula), mientras que title levanta la primera letra de cada palabra (deDe, torreTorre). upper / lower cambian todo. Las letras acentuadas se transforman igual que las demás: Éé, Íí.

Una palabra termina en un espacio, y en ningún otro sitio

title corta solo por espacios. Un guion o un apóstrofo es un carácter corriente dentro de la palabra, así que la letra que va detrás se queda como estaba — justo donde los nombres de persona se apartan de la regla:

josé-maría o'donnell -> José-maría O'donnell
mary-jane watson -> Mary-jane Watson

José-María y O'Donnell es lo que espera quien lee, y no es lo que sale. Lo mismo vale para el atributo case="title" de <gen>, que ejecuta la misma transformación. Cuando la columna es el nombre de una persona, sáquelo de un paquete: el paquete ya lo guarda con las mayúsculas que ese idioma escribe, en vez de aplicar title a un texto en minúsculas.

Reestructuración

Estas cinco etiquetas reacomodan los caracteres de un string. Todas están disponibles también como filtros ${{X | …}} (vea Máscaras y mayúsculas); la forma de etiqueta de compute que se muestra aquí es la que se usa cuando el reacomodo es un paso dentro de un cálculo.

<mask> — dividir y reacomodar según un patrón

Toma un string más pattern=da un string. No se inventa nada: cada carácter del resultado o vino de la entrada o es un literal del patrón.

<mask pattern="…"> reconstruye un string según un patrón posicional: corre de izquierda a derecha, cada ranura se come un pedazo de la entrada, y todo lo demás se imprime como literal.

RanuraToma de la entrada
xun carácter
wuna palabra (letras hasta un espacio) y se traga un espacio
*toda la entrada restante
\escapa el siguiente carácter (\x → una x literal)
x[2] w[1]un pedazo POR POSICIÓN, así que puede reordenarse o repetirse
cualquier otra cosaun literal: guion, punto, espacio, corchetes — se imprime tal cual

Las formas con índice son las mismas que toma el filtro mask:x[0..2] para un rango, x[-1] contando desde el final — y la guía de máscaras las recorre una por una. pattern="x[2]x[1]x[0]" sobre ABC da CBA.

pattern es el único atributo (y es obligatorio); check lo lee y rechaza un patrón que no pueda analizar. Lo tolerante es la entrada: si x / w se pasan de su final no imprimen nada, y cualquier cola sobrante se descarta a menos que un * la recoja.

Úselo cuando un valor llegue como un solo string pegado y usted necesite grupos y separadores — un número de seguro social, un número de tarjeta, un teléfono. Los mismos dígitos bajo un patrón:

<sequence name="Raw"><gen type="text" value="378984323,889735724,852139753,263243158" order="sequential"/></sequence>
<sequence name="Masked">
<compute><result><mask pattern="xxx-xx-xxxx"><field name="Raw"/></mask></result></compute>
</sequence>
...
<data>${{Raw}} -> ${{Masked}}</data>
./run mask-ssn.tdc
378984323  ->  378-98-4323
889735724  ->  889-73-5724
852139753  ->  852-13-9753
263243158  ->  263-24-3158

Cada x toma un carácter; - y el espacio son literales — el patrón se lee xxx-xx-xxxx.

La ranura w — trabajar por palabras. w agarra una palabra y se traga el espacio que va después, así que no queda ninguna sangría suelta:

<sequence name="Full"><gen type="text" value="juan miguel molina,maría jimena rojas,ana de la torre" 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
juan miguel molina  ->  juan: miguel molina
maría jimena rojas  ->  maría: jimena rojas
ana de la torre     ->  ana: de la torre

La primera w toma juan y se come el espacio que le sigue, se imprime el literal : , y * barre con el resto. No hay espacio antes del : — la w se lo tragó.

Escapar — una w literal. Para imprimir un carácter de ranura tal cual, escápelo con \:

<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 es la letra w literal (no una ranura), luego el - literal, y luego cuatro x toman cuatro caracteres.

<slice> — substring por índices

Toma un string más from= y un to= opcional → da un string. La cuenta empieza en 0, un from negativo cuenta desde el final, y un rango más allá del final da un string vacío en vez de un error. Si se omite from= vale 0, así que un <slice> con solo to= corta desde el principio.

<slice from="…" to="…"> corta un substring sobre el rango semiabierto [from, to): el carácter en from se incluye, el carácter en to no — así que la longitud es to − from. Los índices empiezan en cero y se cuentan en puntos de código (de modo que Unicode se corta por letras, no por bytes).

AtributoObligatorioDefine
fromnoíndice del primer carácter (empieza en cero), incluido; omitido → 0
tonoíndice justo después del último carácter; si se omite → hasta el final

Úselo cuando necesite una parte fija de un valor — el año o el mes de una fecha, el prefijo de un código, los últimos caracteres de un identificador.

<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 toma los caracteres 0–3 (el año); from=5 to=7 toma 5–6 (el mes — el guion en el índice 4 no se incluye); from=5 sin to corre del índice 5 hasta el final.

<replace> — reemplazar todas las apariciones

Toma un string más from= y to=da un string. from= se compara literalmente, no como expresión regular, y se reemplazan todas las apariciones.

<replace from="…" to="…"> reemplaza todas las apariciones del substring literal from por to. Ambos son strings simples, no expresiones regulares — lo que usted escriba se busca tal cual. Si from está vacío, o no se encuentra, el string se devuelve sin cambios.

AtributoObligatorioDefine
fromnoqué buscar (literal); omitido → no se reemplaza nada
tonocon qué reemplazarlo; omitido → cada coincidencia se elimina

Úselo cuando necesite cambiar un separador (guion → diagonal en una fecha) o quitar un carácter por completo (to="" elimina cada coincidencia):

<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

Se reemplazan los dos guiones, no solo el primero. Para reemplazar por posición en vez de por substring, use <mask>.

<trim> — quitar los espacios de las orillas

Toma un string → da un string. Solo se tocan las orillas; los espacios de adentro se quedan.

<trim> quita los espacios en blanco de ambas orillas de un string. Los espacios interiores se quedan intactos. No toma atributos.

Úselo cuando los valores de un archivo o un CSV traigan espacios sobrantes en las orillas. Aquí los espacios se agregan a propósito (imitando datos de origen sucios) y los corchetes solo sirven para hacerlos visibles:

<sequence name="City"><gen type="text" value="Monterrey,Bogotá,Sevilla" 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
[  Monterrey   ]  ->  [Monterrey]
[  Bogotá   ]  ->  [Bogotá]
[  Sevilla   ]  ->  [Sevilla]

<group> — agrupar caracteres desde la derecha

Toma un string más size= y sep=da un string. La agrupación va de derecha a izquierda, así que el grupo corto queda a la izquierda, como se escribe el dinero.

<group size="…" sep="…"> parte un string en grupos de size caracteres e inserta sep entre ellos. La agrupación corre de derecha a izquierda, así que el grupo corto (incompleto) queda a la izquierda, igual que con los separadores de miles. Un string más corto que un grupo se devuelve sin cambios (no se agrega separador).

AtributoObligatorioPor omisiónDefine
sizeno3tamaño del grupo (caracteres)
sepnoespacio " "separador entre grupos

Úselo cuando un número largo sea ilegible — dígitos de miles, bloques de una tarjeta, códigos largos:

<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

Como la agrupación corre desde la derecha, 1234567 se parte como 1 234 567 — el 1 sobrante se queda a la izquierda. group size="3" da miles (con el separador de espacio por omisión), sep="-" define su propio separador, y size="4" se lee como bloques de tarjeta. Un 42 corto es más chico que un grupo, así que regresa tal cual. Para agrupar desde la izquierda o por posición, use <mask>.

<pad> — rellenar a la izquierda hasta un ancho fijo

Toma un valor más width= y fill=da un string. width es un mínimo: un valor que ya mide eso o más pasa intacto, nunca se recorta. Si omite width=, la etiqueta no hace absolutamente nada — el valor pasa tal cual, algo fácil de pasar por alto cuando una columna rellenada vuelve sin rellenar.

<pad width="…" fill="…"> convierte su hijo a string y le antepone fill por la izquierda hasta que el string alcanza width. Se comporta como padStart: si el string ya mide al menos width, se devuelve sin cambios — no hay truncamiento.

AtributoPor omisiónDefine
widthancho objetivo del string
fill"0"el carácter que se antepone por la izquierda

Úselo cuando un identificador necesite un ancho fijo — un número de control, un código de artículo, un dígito verificador que debe ocupar dos lugares (09). Ceros a la izquierda hasta un ancho de 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 puede ser cualquier carácter, y un valor más largo que width se deja en paz:

<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" se rellena a un ancho de 6 con ceros o con asteriscos; "1234567" ya es más largo que 4, así que pasa de largo — sin truncamiento. A diferencia de mask / case / slice / group, <pad> no tiene forma de filtro ni atributo de <gen> — funciona únicamente como etiqueta de compute.

El mismo comportamiento, de tres maneras

El formato está disponible como etiqueta de compute, como atributo de <gen> (mask= / case=) y como filtro de interpolación (${{X | mask:…}}) — con el mismo resultado. La guía Máscaras y mayúsculas muestra cada ruta con salida real. Dónde vive cada etiqueta:

OperaciónFiltro ${{X|…}}Atributo de <gen>Etiqueta de <compute>
caso (uppertitle)case=<upper>
maskmask=<mask>
slice / replace / trim / group<slice>
concat / str / padesta página (solo compute)

Eche mano de la etiqueta de compute cuando el formato sea un paso dentro de un cálculo; eche mano del atributo o del filtro para un valor sencillo.

Vea también