Cadenas y caracteres
Almacena y manipula textos.
Una cadena es una serie de caracteres, tales como "Hola, mundo"
o "manzana"
. En Swift, las cadenas están representadas mediante el tipo String
. Es posible acceder al contenido de una cadena de varias formas, incluso como una colección de valores de tipo Character
.
Los valores de tipo String
y Character
de Swift brindan una manera rápida —y compatible con Unicode—, de trabajar con texto en tu código. La sintaxis para crear y manipular cadenas es ligera y legible, con una sintaxis de literales de cadena similar a la de C. La concatenación de cadenas es tan simple como combinar dos cadenas con el operador +
, y la mutabilidad de cadenas se determina al elegir entre una constante o una variable, tal como se hace con cualquier otro valor en Swift. También puedes usar cadenas para insertar constantes, variables, literales, y expresiones en cadenas más largas, mediante un proceso conocido como «interpolación de cadenas». Esto facilita la creación de valores de cadena personalizados para la muestra, almacenamiento e impresión de datos.
Aún con la simplicidad de su sintaxis, el tipo String
de Swift es una implementación rápida y moderna de las cadenas. Cada cadena está compuesta por caracteres Unicode de codificación independiente, y proporciona soporte para acceder a dichos caracteres en diversas representaciones Unicode.
Literales de cadena
Puedes incluir valores predeterminados de tipo String
en tu código como «literales de cadena». Un literal de cadena es una secuencia de caracteres encerrada en comillas dobles ("
).
Usa un literal de cadena como el valor inicial para una constante o variable:
let algunaCadena = "Un valor cualquiera para un literal de cadena"
Nota que Swift infiere un valor de tipo String
para la constante algunaCadena
ya que esta se inicializó con un valor literal de cadena.
Literales de cadena de varias líneas
Si tu literal de cadena necesita múltiples líneas, usa un literal de cadena de varias líneas: una secuencia de caracteres rodeada por tres comillas dobles ("""
):
let cita = """
El Conejo Blanco se puso sus lentes. «¿Por dónde debo empezar,
con la venia de Su Majestad?», preguntó.
«Empieza por el principio», dijo el rey con gravedad, «y sigue
hasta llegar al final; allí te detienes.
"""
Un literal de cadena de varias líneas incluye todas las líneas contenidas entre sus comillas de apertura y de cierrre. La cadena comienza en la primera línea después de las comillas de apertura ("""
) y finaliza en la línea que precede a las comillas de cierre; lo cual significa, que ninguna de las cadenas a continuación, comienza o finaliza, con un salto de línea:
let cadenaDeUnaSolaLinea = "Estas cadenas son iguales."
let cadenaDeVariasLineas = """
Estas cadenas son iguales.
"""
Cuando tu código fuente incluya saltos de línea dentro de un literal de cadena de varias líneas, dichos saltos de línea también formarán parte del valor de la cadena. Si quieres usar saltos de línea para hacer que tu código fuente resulte más legible, pero no quieres que estos formen parte parte del valor de la cadena, escribe una barra invertida (\
) al final de cada línea:
let citaModificada = """
El Conejo Blanco se puso sus lentes. «¿Por dónde debo empezar, \
con la venia de Su Majestad?», preguntó.
«Empieza por el principio», dijo el rey con gravedad, «y sigue \
hasta llegar al final; allí te detienes."
"""
Para crear un literal de cadena de varias líneas que comience o termine con un salto de línea, escribe una línea en blanco como la primera o última línea. Por ejemplo:
let saltosDeLinea = """
Esta cadena comienza con un salto de línea.
También termina con un salto de línea.
"""
A una cadena de varias líneas puede se le puede agregar sangría («indentation») para que coincida con el código a su alrededor. Los espacios en blanco antes de las comillas de cierre ("""
), le indican a Swift el espacio en blanco a ignorar al inicio de cada una de las otras líneas. Sin embargo, si —aparte del espacio en blanco agregado antes de las comillas de cierre—, también escribes algún espacio en blanco al inicio de una línea, dicho espacio en blanco sí será incluido como parte de la cadena.
En el ejemplo anterior, aún cuando todo el literal de cadena de varias líneas tiene una sangría de cuatro espacios, las primera y última líneas en la cadena no comienzan con ningún espacio en blanco. La línea del medio tiene más sangría que las comillas de cierre, por lo que comienza con esa sangría adicional de cuatro espacios.
Caracteres especiales en literales de cadena
Los literales de cadena pueden incluir los siguientes caracteres especiales:
- Los caracteres especiales de escape:
\0
(carácter nulo),\\
(barra invertida),\t
(tabulador),\n
(salto de línea),\r
(retorno de carro),\"
(comilla doble) y\'
(comilla simple) - Un valor escalar Unicode arbitrario, escrito como
\u{_n_}
, donde n es un número hexadecimal, de uno a ocho dígitos (Unicode se discute más adelante en Unicode).
El código a continuación muestra cuatro ejemplos de estos caracteres especiales. La constante palabrasSabias
contiene dos comillas dobles de escape. Las constantes signoDolar
, corazonNegro
, y corazonBrillante
demuestran el formato escalar de Unicode:
let palabrasSabias = "\"La imaginación es más importante que el conocimiento.\" — Einstein"
// "La imaginación es más importante que el conocimiento." — Einstein
let signoDolar = "\u{24}" // $, Escalar Unicode U+0024
let corazonNegro = "\u{2665}" // ♥, Escalar Unicode U+2665
let corazonBrillante = "\u{1F496}" // 💖, Escalar Unicode U+1F496
Dado que los literales de cadena de varias líneas usan tres comillas dobles en lugar de una, es posible incluir comillas dobles ("
) sin escaparlas. Sin embargo, para insertar tres comillas dobles consecutivas ("""
) en una cadena de varias líneas, tendrás que escapar, al menos, una de las comillas. Por ejemplo:
let tresComillasDobles = """
Escapando la primera comilla \"""
Escapando las tres comillas \"\"\"
"""
Delimitadores de cadena extendidos
Puedes colocar un literal de cadena dentro de un «delimitador extendido» para incluir caracteres especiales en la cadena sin invocar sus efectos. Para esto, colocas la cadena dentro de comillas dobles ("
) y luego, rodeas la cadena de signos de número (#
). Por ejemplo, al imprimir el literal de cadena #"Línea 1\nLínea 2"#
, se imprime la secuencia del carácter de escape de salto de línea (\n
), en lugar de imprimirse la cadena en dos líneas.
Si necesitas los efectos especiales de un carácter en un literal de cadena, haz que la cantidad de signos de número coincida con el número de signos de número dentro de la cadena, después del carácter de escape (\
). Por ejemplo, si tu cadena es #"Línea 1\nLínea 2"#
y quieres un salto de línea, puedes usar #"Línea 1\#nLínea 2"#
. De manera similar, ###"Línea 1\###nLínea 2"###
también incluye un salto de línea.
Los literales de cadena creados mediante delimitadores extendidos también pueden ser literales de cadena de varias líneas. Puedes usar delimitadores extendidos para incluir el texto """
en una cadena de varias líneas, anulando el comportamiento predeterminado que da por terminado el literal. Por ejemplo:
let tresComillasDoblesMas = #"""
Aquí hay tres comillas dobles más: """
"""#
Inicializando una cadena vacía
Para crear una cadena vacía como punto inicial para construir una cadena más larga, bien puedes asignar un literal de cadena vacío a una variable o inicializar una nueva instancia del tipo String
mediante sintaxis de inicializador:
var cadenaVacia = "" // literal de cadena vacío
var otraCadenaVacia = String() // sintaxis de inicializador
// Estas dos cadenas están vacías y son equivalentes entre sí.
Verifica si un valor de tipo String
está vacío, al evaluar su propiedad booleana isEmpty
:
if cadenaVacia.isEmpty {
print("Nada que ver por aquí.")
}
// Imprime "Nada que ver por aquí."
Mutabilidad de una cadena
Para indicar si una cadena en particular puede ser modificada (es decir, para hacerla «mutable»), puedes asignarla a una variable (en cuyo caso, podrá ser modificada), o a una constante (en cuyo caso, no podrá ser modificada):
var cadenaVariable = "Caballo"
cadenaVariable += " y carruaje"
// Ahora, cadenaVariable es "Caballo y carruaje"
let cadenaConstante = "Highlander"
cadenaConstante += " y otro Highlander"
// Esto resulta en un error en tiempo de compilación:
// una cadena constante no puede ser modificada
Las cadenas son tipos de valor
El tipo String
en Swift es un «tipo de valor». Al crear un nuevo valor de tipo String
, dicho valor de tipo String
es copiado al pasarlo a una función o método, o al asignarlo a una constante o variable. En cada caso, una nueva copia de la cadena existente es creada y, esa nueva copia, es la que se pasa o asigna, más no la versión original. Los tipos de valor se describen en Las estructuras y enumeraciones son tipos de valor.
El mecanismo de «copia-por-defecto» del tipo String
en Swift garantiza que cuando una función o un método te pasa un valor de tipo String
, queda claro que ese exacto valor de tipo String
es tuyo, independientemente de su procedencia. Puedes tener la certeza de que la cadena que se te pasa no se modificará, a menos que, la modifiques tú mismo.
Detrás de cámaras, el compilador de Swift optimiza el uso de cadenas, de manera que, las copias, como tal, tienen lugar solo al ser absolutamente necesario. Esto significa que siempre tendrás excelente desempeño a la hora de trabajar con cadenas como tipos de valor.
Trabajando con caracteres
Puedes acceder a los valores individuales de tipo Character
en una cadena al iterar sobre la misma mediante un ciclo for-in
:
for caracter in "¡Perro! 🐶" {
print(caracter)
}
// ¡
// P
// e
// r
// r
// o
// !
//
// 🐶
El ciclo for-in
se describe en Ciclos for-in
.
De manera alterna, puedes crear una constante o variable individual de tipo Character
a partir de un literal de cadena, con un solo carácter, agregando una anotación de tipo Character
:
let signoDeExclamacion: Character = "!"
Los valores de tipo String
se pueden construrir pasando un array de valores de tipo Character
como un argumento para su inicializador:
let caracteresGato: [Character] = ["¡", "G", "a", "t", "o", "!", " ", "🐱"]
let cadenaGato = String(caracteresGato)
print(cadenaGato)
// Imprime "¡Gato! 🐱"
Concatenación de cadenas y caracteres
Puedes juntar (o «concatenar») valores de tipo String
con el operador de adición (+
) para crear una nueva cadena:
let cadena1 = "Buenos"
let cadena2 = " días"
var saludo = cadena1 + cadena2
// Ahora, saludo es igual a "Buenos días"
También puedes agregar un valor de tipo String
a una variable existente, de tipo String
mediante el operador de adición-asignación (+=
):
var instruccion = "deletrea la palabra"
instruccion += cadena2
// Ahora, instruccion es igual a "deletrea la palabra días"
Puedes agregar un valor de tipo Character
a una variable String
usando el método append()
del tipo String
:
let punto: Character = "."
saludo.append(punto)
// Ahora, saludo es igual a "Buenos días."
Si estás usando literales de cadena de varias líneas para componer las líneas de una cadena más larga, querrás que cada línea de la cadena finalice con un salto de línea, incluyendo la última línea. Por ejemplo:
let malComienzo = """
uno
dos
"""
let final = """
tres
"""
print(malComienzo + final)
// Imprime dos líneas:
// uno
// dostres
let buenComienzo = """
uno
dos
"""
print(buenComienzo + final)
// Imprime tres líneas:
// uno
// dos
// tres
En el código anterior, concatenar malComienzo
con final
produce una cadena de dos líneas, lo cual no representa el resultado deseado. Dado que la última línea de malComienzo
no finaliza con un salto de línea, esa línea se combina con la primera línea de final
. En contraste, ambas líneas de buenComienzo
terminan con un salto de línea, por lo que al combinarse con final
, el resultado contiene tres líneas, lo cual sí representa el resultado deseado.
Interpolación de cadenas
La interpolación de cadenas es una forma de crear nuevos valores de tipo String
a partir de la combinación de constantes, variables, literales, y expresiones, al incluir sus valores en un literal de cadena. Puedes usar la interpolación de cadenas tanto para literales de cadena de una sola línea como para aquellos de varias líneas. Cada elemento que quieras insertar en el literal de cadena, va rodeado por paréntesis y precedido por una barra invertida (\
):
let multiplicador = 3
let mensaje = "\(multiplicador) por 2.5 es \(Double(multiplicador) * 2.5)"
// mensaje es "3 por 2.5 es 7.5"
En el ejemplo anterior, el valor de multiplicador
se inserta en un literal de cadena como \(multiplicador)
. Este marcador de posición («placeholder») es reemplazado por el valor real de multiplicador
al momento de evaluar la interpolación de cadenas para crear una nueva cadena.
El valor de multiplicador
también forma parte de una expresión más larga, más adelante en la cadena. Esta expresión calcula el valor de Double(multiplicador) * 2.5
e inserta el resultado (7.5
) en la cadena. En este caso, la expresión se escribe como \(Double(multiplicador) * 2.5)
a la hora de incluirla en el literal de cadena.
Puedes usar delimitadores de cadena extendidos para crear cadenas que contengan caracteres que, de otra manera, serían tratados como interpolación de cadenas. Por ejemplo:
print(#"Escribe una cadena interpolada en Swift usando \(multiplicador)."#)
// Imprime "Escribe una cadena interpolada en Swift usando \(multiplicador)."
Para usar interpolación de cadenas dentro de una cadena que usa delimitadores extendidos, haz coincidir la cantidad de signos de número después de la barra invertida con la cantidad de signos de número al principio y al final de la cadena.Por ejemplo:
print(#"6 por 7 es \#(6 * 7)."#)
// Imprime "6 por 7 es 42."
Unicode
Unicode es un estándar internacional para codificar, representar, y procesar texto en diferentes sistemas de escritura. Unicode permite representar casi cualquier carácter de cualquier idioma en una forma estandarizada, así como leer y escribir esos caracteres desde y hacia una fuente externa, como un archivo de texto o una página web. Los tipos String
y Character
de Swift son totalmente compatibles con Unicode, como se describe en esta sección.
Valores escalares Unicode
Detrás de cámaras, el tipo nativo String
de Swift se compone de valores escalares Unicode. Un valor escalar Unicode es un número único de 21 bits para un carácter o modificador, como U+0061
para LATIN SMALL LETTER A
("a"
) o U+1F425
para FRONT-FACING BABY CHICK
("🐥"
).
Ten en cuenta que no todos los valores escalares Unicode de 21 bits se asignan a un carácter, algunos están reservados para asignaciones futuras o para su uso en la codificación UTF-16. Usualmente, los valores escalares que han sido asignados a un carácter también tienen un nombre, como LATIN SMALL LETTER A
y FRONT-FACING BABY CHICK
en el ejemplo anterior.
Grupos de grafemas extendidos
Cada instancia del tipo Character
de Swift representa un solo grupo de grafemas extendidos. Un grupo de grafemas extendidos es una secuencia de uno o más escalares Unicode que (al combinarse) producen un solo carácter legible por humanos.
Acá hay un ejemplo. La letra é
puede ser representada como el escalar Unicode singlular é
(LATIN SMALL LETTER E WITH ACUTE
o U+00E9
). Sin embargo, la misma letra también se puede representar como un par de escalares, una letra e
(LATIN SMALL LETTER E
o U+0065
) base, seguida del escalar `
(COMBINING ACUTE ACCENT
o U+0301
). El escalar COMBINING ACUTE ACCENT
se aplica gráficamente al escalar que le precede, convirtiendo una e
en una é
, cuando es renderizado por un sistema de representación de texto compatible con Unicode.
En ambos casos, la letra é
es representada como un único valor del tipo Character
de Swift que representa un grupo de grafemas extendidos. En el primer caso, el grupo contiene un solo escalar; en el segundo caso, es un grupo de dos escalares:
let eTildada: Character = "\u{E9}" // é
let eTildadaCombinada: Character = "\u{65}\u{301}" // e seguida de ´
// eTildada es é, eTildadaCombinada es é
Los grupos de grafemas extendidos son una forma flexible de representar muchos caracteres tipográficos complejos como un solo valor de tipo Character
. Por ejemplo, las sílabas «Hangul» del alfabeto coreano pueden representarse como una secuencia precompuesta o descompuesta. En Swift, ambas representaciones califican como un único valor de tipo Character
:
let precompuesto: Character = "한" // 한
let descompuesto: Character = "\u{1112}\u{1161}\u{11AB}" // ᄒ, ᅡ, ᆫ
// precompuesto es 한, descompuesto es 한
Los grupos de grafemas extendidos le permiten a los escalares usar símbolos circundantes (tales como COMBINING ENCLOSING CIRCLE
o U+20DD
) para encerrar otros escalares Unicode como parte de un solo valor Character
:
let eTildadaEncerrada: Character = "\u{E9}\u{20DD}"
// eTildadaEncerrada es é⃝
Los escalares Unicode para símbolos indicadores regionales se pueden combinar en pares para formar un único valor Character
, tal como la combinación de REGIONAL INDICATOR SYMBOL LETTER U
(U+1F1FA
) y REGIONAL INDICATOR SYMBOL LETTER S
(U+1F1F8
):
let indicadorRegionalParaUSA: Character = "\u{1F1FA}\u{1F1F8}"
// indicadorRegionalParaUSA es 🇺🇸
Conteo de caracteres
Para obtener la cuenta de valores tipo Character
en una cadena, usa la propiedad count
de la cadena:
let animalesDomesticos = "Gato 🐈, Perro 🐕, Vaca 🐄, Caballo 🐎"
print("animalesDomesticos tiene \(animalesDomesticos.count) caracteres")
// Imprime "animalesDomesticos tiene 34 caracteres"
Ten en cuenta que el uso que hace Swift de los grupos de grafemas extendidos para valores Character
significa que la concatenación y modificación de cadenas puede que no siempre afecten la cuenta de los caracteres en una cadena.
Por ejemplo, si inicializas una nueva cadena con la palabra (de cuatro caracteres) cafe
, y luego agregas `
(COMBINING ACUTE ACCENT
o U+0301
) al final de la cadena, el conteo de caracteres de la cadena resultante seguirá siendo 4
, teniendo en el cuarto carácter, la letra é
, en vez de e
:
var palabra = "cafe"
print("El número de caracteres en \(palabra) es \(palabra.count)")
// Imprime "El número de caracteres en cafe es 4"
palabra += " \u{301}" // COMBINING ACUTE ACCENT o U+0301
print("El número de caracteres en \(palabra) es \(palabra.count)")
// Imprime "El número de caracteres en «café» es 4"
Acceso y modificación de una cadena
Puedes acceder o modificar una cadena, mediante sus métodos y propiedades, o usando sintaxis de «subscript».
Índices de una cadena
Cada valor de tipo String
tiene un tipo de índice asociado, String.Index
, el cual corresponde a la posición de cada Character
en la cadena.
Como se mencionó anteriormente, diferentes caracteres pueden requerir diferentes cantidades de memoria para su almacenamiento, por lo que para poder determinar cuál Character
se encuentra en una posición en particular, debes iterar sobre cada escalar Unicode desde el comienzo o final de la cadena. Es por esta razón que las cadenas de Swift no pueden ser indexadas con valores enteros.
Usa la propiedad startIndex
para acceder a la posición del primer Character
de una cadena. La propiedad endIndex
es la posición que le sigue al último carácter de una cadena. Como resultado, la propiedad endIndex
no es un argumento válido para el «subscript» de una cadena. Si una cadena está vacía, startIndex
y endIndex
serán iguales.
Puedes acceder a los índices anterior y posterior a un índice dado, usando los métodos index(before:)
e index(after:)
del tipo String
. Para acceder a un índice más allá del índice dado, puedes usar el método index(_:offsetBy:)
en lugar de llamar alguno de los métodos anteriores múltiples veces.
Puedes usar sintaxis de «subscript» para acceder al Character
en un índice en particular de una cadena.
let saludo = "¡Buenos dias!"
saludo[saludo.startIndex]
// ¡
saludo[saludo.index(before: saludo.endIndex)]
// !
saludo[saludo.index(after: saludo.startIndex)]
// B
let index = saludo.index(saludo.startIndex, offsetBy: 8)
saludo[index]
// d
Si intentas acceder a un índice fuera del rango de la cadena o a un Character
en un índice fuera del rango de la cadena, generarás un error de tiempo de ejecución.
saludo[saludo.endIndex] // Error
saludo.index(after: saludo.endIndex) // Error
Usa la propiedad indices
para acceder a todos los índices de los caracteres individuales de una cadena.
for index in saludo.indices {
print("\(saludo[index]) ", terminator: "")
}
// Imprime "¡ B u e n o s d i a s ! "
Insertar y remover
Para insertar un solo carácter en una cadena en un índice en específico, usa el método insert(_:at:)
, y para insertar el contenido de otra cadena en un índice en específico, usa el método insert(contentsOf:at:)
.
var saludoDeBienvenida = "Buenas"
saludoDeBienvenida.insert("!", at: saludoDeBienvenida.endIndex)
// Ahora, saludoDeBienvenida es igual a «Buenas!»
saludoDeBienvenida.insert(contentsOf: " tardes", at: saludoDeBienvenida.index(before: saludoDeBienvenida.endIndex))
// Ahora, saludoDeBienvenida es igual a «Buenas tardes!»
Para remover un solo carácter de una cadena en un índice en particular, usa el método remove(at:)
, y para remover una subcadena en un rango específico, usa el método removeSubrange(_:)
:
saludoDeBienvenida.remove(at: saludoDeBienvenida.index(before: saludoDeBienvenida.endIndex))
// Ahora, saludoDeBienvenida es igual a «Buenas tardes»
let range = saludoDeBienvenida.index(saludoDeBienvenida.endIndex, offsetBy: -7)..<saludoDeBienvenida.endIndex
saludoDeBienvenida.removeSubrange(range)
// Ahora, saludoDeBienvenida es igual a «Buenas»
Subcadenas
Al formar una subcadena a partir de una cadena —por ejemplo, mediante un «subscript» o un método como prefix(_:)
—, el resultado es una instancia del tipo Substring (opens in a new tab), más no otra cadena. Las subcadenas en Swift tienen la mayoría de los mismos métodos que las cadenas, lo que significa que puedes trabajar con subcadenas de la misma manera en que trabajas con cadenas. Sin embargo, a diferencia de las cadenas, solo utilizas subcadenas por un corto período de tiempo al ejecutar acciones sobre una cadena. Cuando estés listo para almacenar el resultado por un período más extenso, convierte la subcadena en una instancia de tipo String
. Por ejemplo:
let saludo = "Hola, mundo."
let indice = saludo.firstIndex(of: ",") ?? saludo.endIndex
let comienzo = saludo[..<indice]
// comienzo es "Hola"
// Se convierte el resultado a tipo String para almacenamiento a largo plazo.
let nuevaCadena = String(comienzo)
Al igual que las cadenas, cada subcadena tiene una región de memoria donde se almacenan los caracteres que conforman la subcadena. La diferencia entre cadenas y subcadenas es que, para efectos de optimización de rendimiento, una subcadena puede reutilizar parte de la memoria que se usa para almacenar la cadena original, o parte de la memoria que se usa para almacenar otra subcadena (las cadenas cuentan con una optimización similar, pero si dos cadenas comparten una región de memoria, estas son iguales). Esta optimización de rendimiento significa que no tienes que pagar el costo de rendimiento que implica la copia de memoria hasta que no modifiques la cadena o la subcadena. Como se mencionó anteriormente, las subcadenas no son adecuadas para el almacenamiento a largo plazo, ya que estas reutilizan el almacenamiento de la cadena original, es decir, toda la cadena original deberá mantenerse en memoria siempre que cualquiera de sus subcadenas sea utilizada.
En el ejemplo anterior, saludo
es una cadena, lo que significa que tiene una región de memoria donde se almacenan los caracteres que conforman la cadena. Como comienzo
es una subcadena de saludo
, esta reutiliza la memoria ocupada por saludo
. En contraste, nuevaCadena
es una cadena; al ser creada a partir de la subcadena, cuenta con su propio almacenamiento. La siguiente figura ilustra estas relaciones:
Comparación de cadenas
Swift ofrece tres formas de comparar valores textuales: igualdad de cadenas y caracteres, igualdad de prefijo, e igualdad de sufijo.
Igualdad de cadenas y caracteres
La igualdad de cadenas y caracteres se verifica mediante los operadores «igual que» (==
) y «no igual que» (!=
), como se describe en Operadores de comparación:
let frase = "Somos muy parecidos. Tú y yo."
let mismaFrase = "Somos muy parecidos. Tú y yo."
if frase == mismaFrase {
print("Estas dos cadenas son consideradas iguales.")
}
// Imprime "Estas dos cadenas son consideradas iguales."
Dos valores de tipo String
(o de tipo Character
) se consideran iguales si sus grupos de grafemas extendidos son «canónicamente equivalentes». Dos grupos de grafemas extendidos son canónicamente equivalentes si tienen el mismo significado lingüístico y apariencia, incluso si se componen de escalares Unicode diferentes tras bambalinas.
Por ejemplo, LATIN SMALL LETTER E WITH ACUTE
(U+00E9
) es canónicamente equivalente a LATIN SMALL LETTER E
(U+0065
) seguida de COMBINING ACUTE ACCENT
(U+0301
). Ambos grupos de grafemas extendidos son formas válidas para representar el carácter é
, y por lo tanto, son considerados canónicamente equivalentes:
// "¿Te apetece un café?" usando LATIN SMALL LETTER E WITH ACUTE
let preguntaConEAcentuada = "¿Te apetece un caf\u{E9}?"
// "¿Te apetece un café?" usando LATIN SMALL LETTER E y COMBINING ACUTE ACCENT
let preguntaConEAcentuadaCombinada = "¿Te apetece un caf\u{65}\u{301}?"
if preguntaConEAcentuada == preguntaConEAcentuadaCombinada {
print("Estas dos cadenas son consideradas iguales.")
}
// Imprime "Estas dos cadenas son consideradas iguales."
En contraste, LATIN CAPITAL LETTER A
(U+0041
o "A"
) —como es usada en el idioma inglés—, no es equivalente a CYRILLIC CAPITAL LETTER A
(U+0410
o "А"
), como se usa en el ruso. Los caracteres son visualmente similares, pero no tienen el mismo significado lingüístico:
let letraALatinaMayuscula: Character = "\u{41}"
let letraACirilicaMayuscula: Character = "\u{0410}"
if letraALatinaMayuscula != letraACirilicaMayuscula {
print("Estos dos caracteres no son equivalentes.")
}
// Imprime "Estos dos caracteres no son equivalentes."
Igualdad de prefijo y sufijo
Para verificar si una cadena tiene un prefijo o sufijo de cadena en particular, llama a los métodos hasPrefix(_:)
y hasSuffix(_:)
de la cadena, ambos de los cuales toman un solo argumento de tipo String
y devuelven un valor booleano.
Los ejemplos a continuación consideran un array de cadenas que representan las ubicaciones de las escenas de los dos primeros actos de la obra de Shakespeare, «Romeo y Julieta»:
let romeoYJulieta = [
"Acto 1 Escena 1: Verona. Una plaza pública.",
"Acto 1 Escena 2: La mansión Capuleto.",
"Acto 1 Escena 3: Un cuarto en la mansión Capuleto.",
"Acto 1 Escena 4: Una calle afuera de la mansión Capuleto.",
"Acto 1 Escena 5: El Gran Salón en la mansión Capuleto.",
"Acto 2 Escena 1: Afuera de la mansión Capuleto.",
"Acto 2 Escena 2: El jardín de Capuleto.",
"Acto 2 Escena 3: Afuera de la celda del hermano Lorenzo.",
"Acto 2 Escena 4: Una calle en Verona.",
"Acto 2 Escena 5: La mansión Capuleto.",
"Acto 2 Escena 6: La celda del hermano Lorenzo."
]
Puedes usar el método hasPrefix(_:)
con el array romeoYJulieta
para contar el número de escenas en el Acto 1 de la obra:
var conteoDeEscenasDelActo1 = 0
for escena in romeoYJulieta {
if escena.hasPrefix("Acto 1 ") {
conteoDeEscenasDelActo1 += 1
}
}
print("Hay \(conteoDeEscenasDelActo1) escenas en el Acto 1.")
// Imprime "Hay 5 escenas en el Acto 1."
De manera similar, usa el método hasSuffix(_:)
para contar el número de escenas que tienen lugar en o cerca de la mansión Capuleto y de la celda del hermano Lorenzo:
var conteoMansion = 0
var conteoCelda = 0
for escena in romeoYJulieta {
if escena.hasSuffix("mansión Capuleto.") {
conteoMansion += 1
} else if escena.hasSuffix("celda del hermano Lorenzo.") {
conteoCelda += 1
}
}
print("\(conteoMansion) escenas en la mansión; \(conteoCelda) escenas en la celda")
// Imprime "6 escenas en la mansión; 2 escenas en la celda"
Representación Unicode de cadenas
Al guardar una cadena Unicode en un archivo de texto o algún otro almacenamiento, los escalares Unicode de esa cadena se codifican en uno de los muchos «formatos de codificación» definidos por Unicode. Cada formato codifica la cadena en pequeños segmentos conocidos como «unidades de código». Estos incluyen el formato de codificación UTF-8 (el cual codifica una cadena como unidades de código de 8 bits), el formato de codificación UTF-16 (el cual codifica una cadena como unidades de código de 16 bits), y el formato de codificación UTF-32 (el cual codifica una cadena como unidades de código de 32 bits).
Swift proporciona muchas formas diferentes de acceder a la representación Unicode de una cadena. Puedes iterar sobre la cadena usando una instrucción for-in
, para acceder a sus valores individuales (de tipo Character
) como grupos de grafemas extendidos Unicode. Este proceso se describe en Trabajando con caracteres.
De manera alternativa, puedes acceder a un valor de tipo String
en una de tres representaciones compatibles con Unicode:
- Una colección de unidades de código UTF-8 (usa la propiedad
utf8
de la cadena) - Una colección de unidades de código UTF-16 (usa la propiedad
utf16
de la cadena) - Una colección de valores escalares Unicode de 21 bits, equivalente al formato de codificación UTF-32 de la cadena (usa la propiedad
unicodeScalars
de la cadena)
Cada ejemplo a continuación muestra una representación diferente de la siguiente cadena, la cual se compone de los caracteres P
, e
, z
, ‼
(DOUBLE EXCLAMATION MARK
o escalar Unicode U+203C
), y el carácter 🐠 (TROPICAL FISH
o escalar Unicode U+1F420
):
let cadenaPez = "Pez‼🐠"
Representación UTF-8
Puedes acceder a la representación UTF-8 de una cadena al iterar sobre su propiedad utf8
. Esta propiedad es de tipo String.UTF8View
, que es una colección de valores, sin signo, de 8 bits (UInt8
), uno para cada byte en la representación UTF-8 de la cadena:
for unidadDeCodigo in cadenaPez.utf8 {
print("\(unidadDeCodigo) ", terminator: "")
}
print("")
// Imprime "80 101 122 226 128 188 240 159 144 160 "
En el ejemplo anterior, los primeros tres valores decimales unidadDeCodigo
(80
, 101
, 122
) representan los caracteres P
, e
, y z
, cuya representación es la misma que su representación ASCII. Los siguientes tres valores decimales unidadDeCodigo
(226
, 128
, 188
) son una representación UTF-8, de 3 bytes, del carácter DOUBLE EXCLAMATION MARK
. Los últimos cuatro valores unidadDeCodigo
(240
, 159
, 144
, 160
) son una representación UTF-8, de cuatro bytes, del carácter TROPICAL FISH
.
Representación UTF-16
Puedes acceder a la representación UTF-16 de una cadena al iterar sobre su propiedad utf16
. Esta propiedad es de tipo String.UTF16View
, que es una colección de valores, sin signo, de 16 bits (UInt16
), uno para cada unidad de código de 16 bits en la representación UTF-16 de la cadena:
for unidadDeCodigo in cadenaPez.utf16 {
print("\(unidadDeCodigo) ", terminator: "")
}
print("")
// Imprime "80 101 122 8252 55357 56352 "
Nuevamente, los primeros tres valores unidadDeCodigo
(80
, 101
, 122
) representan los caracteres P
, e
, y z
, cuyas unidades de código UTF-16 tienen los mismos valores que los de la representación UTF-8 de la cadena (ya que estos escalares Unicode representan caracteres ASCII).
El cuarto valor unidadDeCodigo
(8252
) es el decimal equivalente al valor hexadecimal 203C
, el cual representa el escalar Unicode U+203C
para el carácter DOUBLE EXCLAMATION MARK
. Este carácter se puede representar como una sola unidad de código en UTF-16.
Los valores quinto y sexto unidadDeCodigo
(55357
y 56352
) son la representación de los «pares subrogados» (surrogate pair) UTF-16 del carácter TROPICAL FISH
. Estos valores son un valor «subrogado» alto de U+D83D
(valor decimal 55357
) y valor «subrogado» bajo de U+DC36
(valor decimal 56352
).
Representación escalar Unicode
Puedes acceder a la representación escalar Unicode de una cadena al iterar sobre su propiedad unicodeScalars
. Esta propiedad es de tipo UnicodeScalarView
, que es una colección de valores de tipo UnicodeScalar
.
Cada UnicodeScalar
tiene un propiedad con un valor que devuelve el valor escalar de 21 bits, representado dentro de un valor UInt32
:
for escalar in cadenaPez.unicodeScalars {
print("\(escalar.value) ", terminator: "")
}
print("")
// Imprime "80 101 122 8252 128032 "
Las propiedades value
para los primeros tres valores UnicodeScalar
(80
, 101
, 122
) representan, una vez más, los caracteres P
, e
, y z
.
El cuarto valor escalar
(8252
) es, nuevamente, el decimal equivalente al valor hexadecimal 203C
, el cual representa el escalar Unicode U+203C
para el carácter DOUBLE EXCLAMATION MARK
.
La propiedad value
del quinto —y último— UnicodeScalar
, 128032
, es el decimal equivalente al valor hexadecimal 1F436
, el cual representa el escalar Unicode U+1F436
para el carácter TROPICAL FISH
.
Como una alternativa para obtener sus propiedades value
, cada valor UnicodeScalar
puede ser utilizado para construir un nuevo valor de tipo String
, tal como se hace mediante interpolación de cadenas:
for escalar in cadenaPez.unicodeScalars {
print("\(escalar) ")
}
// Imprime
// P
// e
// z
// ‼
// 🐠