Alias y cadenas de caracteres (strings)

Ahora que sabemos lo que son los arrays, que hemos entrado en contacto con immutable y que hemos echado un vistazo rápido a los tipos de datos básicos, es momento de presentar dos nuevas construcciones en una sola línea:

alias string = immutable(char)[];

El término string se define mediante una sentencia alias que lo declara como un slice de caracteres inmutables (immutable(char)). Esto significa que una vez que un string ha sido construido, su contenido nunca cambia. Y esta es la segunda introducción: ¡bienvenidas, strings en UTF-8!

Debido a su inmutabilidad, las strings se puede compartir perfectamente entre varios hilos de ejecución. Como un string es un slice, se pueden coger ciertas partes de la misma sin necesidad de reservar memoria. La función std.algorithm.splitter de la librería estándar, por ejemplo, parte una cadena por cada retorno de carro sin hacer ninguna reserva de memoria.

Además de tener cadenas codificadas mediante UTF-8, existen otros dos tipos más:

alias wstring = immutable(wchar)[]; // UTF-16
alias dstring = immutable(dchar)[]; // UTF-32

Estas variantes se convierten fácilmente unas entre otras usando el método to del paquete std.conv:

dstring myDstring = to!dstring(myString);
string myString   = to!string(myDstring);

Cadenas de caracteres Unicode

Una cadena de caracteres plana (un string) se define como un array de code units (unidades de código) de Unicode, cada uno de un tamaño de 8 bits (un byte). Todas las operaciones aplicables a los arrays se pueden usar sobre las cadenas, ya que trabajan al nivel de las code units y no a nivel de carácter. Por otro lado, la librería estándar interpreta las cadena como secuencias de code points (puntos de código). Además existe otra opción que trata las cadenas como secuencias de graphemes (grafemas) indicándolo de forma explícita. Es la función std.uni.byGrapheme.

Este pequeño ejemplo ilustra las diferencias en la interpretación:

string s = "\u0041\u0308"; // Ä

writeln(s.length); // 3

import std.range : walkLength;
writeln(s.walkLength); // 2

import std.uni : byGrapheme;
writeln(s.byGrapheme.walkLength); // 1

En el ejemplo, la longitud real del array s es 3, ya que contiene 3 unidades de código (code units): 0x41, 0x03 y 0x08. Estos caracteres combinados definen un único punto de código (code point), ya que combina el caracter principal con el acento diacrítico, por lo que la función walkLength (función de la librería estándar que calcula la longitud de un rango) cuenta dos puntos de código (code points) en total. Finalmente, la función byGrapheme realiza costosos cálculos para reconocer que esos dos puntos de código se combinan en único caracter a mostrar.

El procesado correcto de cadenas Unicode puede ser bastante complejo, por eso la mayor parte del tiempo los desarrolladores que usen D pueden simplemente considerar las variables de tipo string como arrays de bytes mágicos y confiar en que los algoritmos de la librería estándar hagan su trabajo correctamente. Si se desea iterar una cadena por unidad de código, se puede usar la función byCodeUnit.

La decodificación de caracteres Unicode en D se explica con más detalle en el capítulo de Unicode.

Cadenas de caracteres multilínea

Las cadenas de caracteres en D siempre se pueden poner en varias líneas:

string multiline = "
Esto
podría ser un
documento grande
";

Cuando aparecen las comillas en un documento, se pueden usar tanto las cadenas wysiwyg (what you see is what you get, lo que ves es lo que obtienes) como las cadenas heredoc.

Cadenas wysiwyg

También es posible usar raw strings (cadenas sin procesar) para minimizar la laboriosa tarea de escapar (escape) símbolos reservados. Las cadenas sin procesar pueden ser declaradas o bien usando acentos graves —backticks en inglés— ( ` ... ` ) o bien usando el prefijo r —de raw— (r" ... ").

string raw  =  `raw "string"`; // cadena sin procesar
string raw2 = r"raw `string`"; // cadena sin procesar

D proporciona incluso más formas de representar cadenas. No dudes en explorarlas.

En profundidad

rdmd playground.d