Guía Completa del Paquete unicode en Go
El paquete unicode en Go proporciona funcionalidades robustas para manejar caracteres Unicode, facilitando operaciones como clasificación, conversión y manipulación de texto en diferentes idiomas. Esta guía explora en profundidad las funciones disponibles con ejemplos prácticos.
Clasificación de Caracteres
Letras y Caracteres Alfanuméricos
package main
import (
"fmt"
"unicode"
)
func main() {
// IsLetter - Verifica si un rune es una letra
fmt.Printf("¿Es 'A' una letra? %t\n", unicode.IsLetter('A'))
fmt.Printf("¿Es 'ñ' una letra? %t\n", unicode.IsLetter('ñ'))
fmt.Printf("¿Es '漢' una letra? %t\n", unicode.IsLetter('漢'))
fmt.Printf("¿Es '5' una letra? %t\n", unicode.IsLetter('5'))
// IsDigit - Verifica específicamente si es un dígito decimal (0-9)
fmt.Printf("¿Es '7' un dígito? %t\n", unicode.IsDigit('7'))
fmt.Printf("¿Es '٧' (dígito árabe) un dígito? %t\n", unicode.IsDigit('٧'))
// IsNumber - Más amplio que IsDigit, incluye otros tipos de números Unicode
fmt.Printf("¿Es '½' un número? %t\n", unicode.IsNumber('½'))
fmt.Printf("¿Es 'Ⅷ' (numeral romano) un número? %t\n", unicode.IsNumber('Ⅷ'))
// Combinando verificaciones para caracteres alfanuméricos
esAlfanumerico := func(r rune) bool {
return unicode.IsLetter(r) || unicode.IsDigit(r)
}
fmt.Printf("¿Es 'A9' alfanumérico? %t y %t\n", esAlfanumerico('A'), esAlfanumerico('9'))
}
Mayúsculas, Minúsculas y Título
package main
import (
"fmt"
"unicode"
)
func main() {
// IsUpper - Verifica si es mayúscula
fmt.Printf("¿'A' es mayúscula? %t\n", unicode.IsUpper('A'))
fmt.Printf("¿'É' es mayúscula? %t\n", unicode.IsUpper('É'))
// IsLower - Verifica si es minúscula
fmt.Printf("¿'a' es minúscula? %t\n", unicode.IsLower('a'))
fmt.Printf("¿'ñ' es minúscula? %t\n", unicode.IsLower('ñ'))
// IsTitle - Detecta caracteres específicos que son mayúsculas de título
// Algunos caracteres especiales tienen forma de título propia
fmt.Printf("¿'Dž' (DZ con acento) es un carácter de título? %t\n", unicode.IsTitle('Dž'))
// Ejemplo práctico: verificar si una palabra comienza con mayúscula
palabra := "España"
primerRuna := []rune(palabra)[0]
fmt.Printf("¿'%s' comienza con mayúscula? %t\n", palabra, unicode.IsUpper(primerRuna))
}
Espacios y Control
package main
import (
"fmt"
"strings"
"unicode"
)
func main() {
// IsSpace - Detecta diferentes tipos de espacios en Unicode
fmt.Printf("Espacio normal: %t\n", unicode.IsSpace(' '))
fmt.Printf("Tabulación: %t\n", unicode.IsSpace('\t'))
fmt.Printf("Salto de línea: %t\n", unicode.IsSpace('\n'))
fmt.Printf("Espacio no rompible: %t\n", unicode.IsSpace('\u00A0'))
// IsControl - Detecta caracteres de control
fmt.Printf("Nulo '\\0': %t\n", unicode.IsControl('\u0000'))
fmt.Printf("Escape '\\e': %t\n", unicode.IsControl('\u001B'))
fmt.Printf("Delete: %t\n", unicode.IsControl('\u007F'))
// Ejemplo práctico: Eliminar espacios y contar palabras
texto := " Hola mundo! ¿Cómo estás? "
palabras := strings.FieldsFunc(texto, unicode.IsSpace)
fmt.Printf("Texto: %q\n", texto)
fmt.Printf("Palabras: %q\n", palabras)
fmt.Printf("Número de palabras: %d\n", len(palabras))
}
Símbolos y Puntuación
package main
import (
"fmt"
"unicode"
)
func main() {
// IsPunct - Identifica signos de puntuación
fmt.Printf("Punto '.': %t\n", unicode.IsPunct('.'))
fmt.Printf("Coma ',': %t\n", unicode.IsPunct(','))
fmt.Printf("Interrogación '?': %t\n", unicode.IsPunct('?'))
fmt.Printf("¿Interrogación de apertura '¿'? %t\n", unicode.IsPunct('¿'))
// IsSymbol - Identifica símbolos
fmt.Printf("Dólar '$': %t\n", unicode.IsSymbol('$'))
fmt.Printf("Porcentaje '%%': %t\n", unicode.IsSymbol('%'))
fmt.Printf("Copyright '©': %t\n", unicode.IsSymbol('©'))
fmt.Printf("Emoji '😊': %t\n", unicode.IsSymbol('😊'))
// Diferencia entre símbolos y puntuación
caracteresVarios := []rune{'@', '#', '+', '=', ':', ';', '(', '[', '"'}
fmt.Println("\nClasificación de caracteres:")
for _, c := range caracteresVarios {
fmt.Printf("%q es: Puntuación=%t, Símbolo=%t\n",
c, unicode.IsPunct(c), unicode.IsSymbol(c))
}
// IsGraphic y IsPrint - Caracteres con representación visual
fmt.Printf("\nTabulación '\\t' es: Gráfico=%t, Imprimible=%t\n",
unicode.IsGraphic('\t'), unicode.IsPrint('\t'))
fmt.Printf("Espacio ' ' es: Gráfico=%t, Imprimible=%t\n",
unicode.IsGraphic(' '), unicode.IsPrint(' '))
fmt.Printf("Letra 'A' es: Gráfico=%t, Imprimible=%t\n",
unicode.IsGraphic('A'), unicode.IsPrint('A'))
}
Transformación de Caracteres
Conversión de Mayúsculas/Minúsculas
package main
import (
"fmt"
"strings"
"unicode"
)
func main() {
// ToUpper - Convertir a mayúsculas
fmt.Printf("'a' → %q\n", unicode.ToUpper('a'))
fmt.Printf("'ñ' → %q\n", unicode.ToUpper('ñ'))
fmt.Printf("'é' → %q\n", unicode.ToUpper('é'))
// ToLower - Convertir a minúsculas
fmt.Printf("'A' → %q\n", unicode.ToLower('A'))
fmt.Printf("'Ñ' → %q\n", unicode.ToLower('Ñ'))
fmt.Printf("'É' → %q\n", unicode.ToLower('É'))
// ToTitle - Convertir a título (similar a mayúscula pero específico para título)
fmt.Printf("'a' a título → %q\n", unicode.ToTitle('a'))
fmt.Printf("'ñ' a título → %q\n", unicode.ToTitle('ñ'))
// SimpleFold - Iterar por el conjunto de equivalencias de plegado
c := 'A'
fmt.Printf("Iterando por plegado de %q: ", c)
for i := 0; i < 3; i++ {
c = unicode.SimpleFold(c)
fmt.Printf("%q ", c)
}
fmt.Println()
// Ejemplo práctico: Convertir strings completos
frase := "¡Hola Mundo!"
mayusculas := strings.Map(unicode.ToUpper, frase)
minusculas := strings.Map(unicode.ToLower, frase)
fmt.Printf("Original: %s\n", frase)
fmt.Printf("Mayúsculas: %s\n", mayusculas)
fmt.Printf("Minúsculas: %s\n", minusculas)
}
Tablas Unicode y Rangos
El paquete unicode define múltiples rangos y tablas que agrupan caracteres por categorías o propiedades.
package main
import (
"fmt"
"unicode"
)
func main() {
// Rangos predefinidos en el paquete unicode
fmt.Println("Rangos Unicode:")
// Letras latinas
fmt.Printf("'A' es letra latina: %t\n", unicode.In('A', unicode.Latin))
fmt.Printf("'α' (alfa griego) es letra latina: %t\n", unicode.In('α', unicode.Latin))
// Letras griegas
fmt.Printf("'α' es letra griega: %t\n", unicode.In('α', unicode.Greek))
fmt.Printf("'Ω' es letra griega: %t\n", unicode.In('Ω', unicode.Greek))
// Caracteres cirílicos
fmt.Printf("'Я' es letra cirílica: %t\n", unicode.In('Я', unicode.Cyrillic))
// Caracteres asiáticos
fmt.Printf("'漢' es un carácter Han: %t\n", unicode.In('漢', unicode.Han))
// Combinar rangos con funciones de clasificación
esLetraLatina := func(r rune) bool {
return unicode.IsLetter(r) && unicode.In(r, unicode.Latin)
}
fmt.Printf("'A' es letra latina: %t\n", esLetraLatina('A'))
fmt.Printf("'α' es letra latina: %t\n", esLetraLatina('α'))
}
Scripts y Propiedades Unicode
package main
import (
"fmt"
"unicode"
)
func main() {
// Usando tablas de propiedades
fmt.Printf("'A' es parte del script latino: %t\n",
unicode.Is(unicode.Latin, 'A'))
fmt.Printf("'漢' es parte del script Han: %t\n",
unicode.Is(unicode.Han, '漢'))
fmt.Printf("'α' es parte del script griego: %t\n",
unicode.Is(unicode.Greek, 'α'))
// Combinando múltiples scripts
caracteresVarios := []rune{'A', 'α', '漢', 'Я', '9', '©'}
for _, c := range caracteresVarios {
fmt.Printf("%q pertenece a: ", c)
if unicode.Is(unicode.Latin, c) {
fmt.Printf("Latino ")
}
if unicode.Is(unicode.Greek, c) {
fmt.Printf("Griego ")
}
if unicode.Is(unicode.Han, c) {
fmt.Printf("Han ")
}
if unicode.Is(unicode.Cyrillic, c) {
fmt.Printf("Cirílico ")
}
fmt.Println()
}
}
Casos Prácticos
Validación de Entrada
package main
import (
"fmt"
"strings"
"unicode"
)
// Validar un nombre de usuario que solo permite letras, números y guiones bajos
func esNombreUsuarioValido(nombre string) bool {
if len(nombre) < 3 || len(nombre) > 20 {
return false
}
for _, r := range nombre {
if !unicode.IsLetter(r) && !unicode.IsDigit(r) && r != '_' {
return false
}
}
// El nombre debe comenzar con una letra
return unicode.IsLetter([]rune(nombre)[0])
}
// Validar una contraseña con requisitos de seguridad
func esContraseñaSegura(pass string) (bool, string) {
if len(pass) < 8 {
return false, "La contraseña debe tener al menos 8 caracteres"
}
var tieneMayuscula, tieneMinuscula, tieneDigito, tieneSimbolo bool
for _, r := range pass {
if unicode.IsUpper(r) {
tieneMayuscula = true
} else if unicode.IsLower(r) {
tieneMinuscula = true
} else if unicode.IsDigit(r) {
tieneDigito = true
} else if unicode.IsPunct(r) || unicode.IsSymbol(r) {
tieneSimbolo = true
}
}
if !tieneMayuscula {
return false, "Debe incluir al menos una letra mayúscula"
}
if !tieneMinuscula {
return false, "Debe incluir al menos una letra minúscula"
}
if !tieneDigito {
return false, "Debe incluir al menos un dígito"
}
if !tieneSimbolo {
return false, "Debe incluir al menos un símbolo"
}
return true, "Contraseña válida"
}
func main() {
// Probar validación de nombre de usuario
nombresUsuario := []string{"usuario123", "Usuario_1", "123user", "us@er", "a"}
fmt.Println("Validación de nombres de usuario:")
for _, nombre := range nombresUsuario {
fmt.Printf("%q es válido: %t\n", nombre, esNombreUsuarioValido(nombre))
}
// Probar validación de contraseña
contraseñas := []string{"abc123", "Password1", "Passw0rd!", "CONTRASEÑA123", "contraseña!"}
fmt.Println("\nValidación de contraseñas:")
for _, pass := range contraseñas {
valida, mensaje := esContraseñaSegura(pass)
fmt.Printf("%q: %s\n", pass, mensaje)
}
}
Procesamiento de Texto
package main
import (
"fmt"
"strings"
"unicode"
)
// Normalizar texto eliminando espacios extras y convirtiendo a minúsculas
func normalizarTexto(texto string) string {
// Convertir a minúsculas
texto = strings.Map(unicode.ToLower, texto)
// Eliminar espacios duplicados
var resultado strings.Builder
espacioAnterior := true // Para manejar espacios al inicio
for _, r := range texto {
if unicode.IsSpace(r) {
if !espacioAnterior {
resultado.WriteRune(' ')
espacioAnterior = true
}
} else {
resultado.WriteRune(r)
espacioAnterior = false
}
}
return strings.TrimSpace(resultado.String())
}
// Contar palabras, separando correctamente por Unicode
func contarPalabras(texto string) int {
palabras := strings.FieldsFunc(texto, unicode.IsSpace)
return len(palabras)
}
// Convertir a formato título (primera letra de cada palabra en mayúscula)
func formatoTitulo(texto string) string {
palabras := strings.FieldsFunc(texto, unicode.IsSpace)
for i, palabra := range palabras {
if len(palabra) > 0 {
runas := []rune(palabra)
runas[0] = unicode.ToTitle(runas[0])
palabras[i] = string(runas)
}
}
return strings.Join(palabras, " ")
}
// Sanitizar texto para URLs (eliminar acentos, espacios, etc.)
func sanitizarParaURL(texto string) string {
texto = strings.Map(func(r rune) rune {
if unicode.IsLetter(r) || unicode.IsDigit(r) {
// Convertir letras a minúsculas
return unicode.ToLower(r)
} else if unicode.IsSpace(r) {
return '-'
}
// Eliminar cualquier otro carácter
return -1
}, texto)
// Eliminar guiones duplicados
for strings.Contains(texto, "--") {
texto = strings.ReplaceAll(texto, "--", "-")
}
return strings.Trim(texto, "-")
}
func main() {
texto := " ¡Hola MUNDO! ¿Cómo estás? 😊 "
fmt.Printf("Original: %q\n", texto)
fmt.Printf("Normalizado: %q\n", normalizarTexto(texto))
fmt.Printf("Palabras: %d\n", contarPalabras(texto))
fmt.Printf("Formato título: %q\n", formatoTitulo(normalizarTexto(texto)))
tituloArticulo := "Cómo Aprender Go: Una Guía Práctica (2023)"
fmt.Printf("\nTítulo original: %q\n", tituloArticulo)
fmt.Printf("URL amigable: %q\n", sanitizarParaURL(tituloArticulo))
}
Rendimiento y Optimizaciones
package main
import (
"fmt"
"strings"
"time"
"unicode"
)
func main() {
// Texto de prueba grande
textoGrande := strings.Repeat("¡Hola Mundo! ¿Cómo estás? 123 αβγ 漢字 ", 10000)
// Método 1: Usar unicode.Is* en cada carácter
inicio := time.Now()
contarCaracteresPorTipo1 := func(texto string) (letras, digitos, espacios, otros int) {
for _, r := range texto {
if unicode.IsLetter(r) {
letras++
} else if unicode.IsDigit(r) {
digitos++
} else if unicode.IsSpace(r) {
espacios++
} else {
otros++
}
}
return
}
l1, d1, e1, o1 := contarCaracteresPorTipo1(textoGrande)
duracion1 := time.Since(inicio)
// Método 2: Combinar búsquedas usando tablas
inicio = time.Now()
contarCaracteresPorTipo2 := func(texto string) (letras, digitos, espacios, otros int) {
for _, r := range texto {
switch {
case unicode.IsLetter(r):
letras++
case unicode.IsDigit(r):
digitos++
case unicode.IsSpace(r):
espacios++
default:
otros++
}
}
return
}
l2, d2, e2, o2 := contarCaracteresPorTipo2(textoGrande)
duracion2 := time.Since(inicio)
fmt.Println("Comparación de rendimiento:")
fmt.Printf("Método 1: %v (Letras: %d, Dígitos: %d, Espacios: %d, Otros: %d)\n",
duracion1, l1, d1, e1, o1)
fmt.Printf("Método 2: %v (Letras: %d, Dígitos: %d, Espacios: %d, Otros: %d)\n",
duracion2, l2, d2, e2, o2)
fmt.Printf("Rendimiento relativo: %.2f%%\n",
float64(duracion1.Nanoseconds())/float64(duracion2.Nanoseconds())*100)
}
Conclusión
El paquete unicode de Go proporciona herramientas robustas para manejar texto internacional y caracteres Unicode de forma eficiente. Es especialmente útil para:
- Validación de entrada de usuario
- Procesamiento y normalización de texto
- Manipulación de cadenas multilingües
- Identificación y clasificación de caracteres
- Operaciones de conversión entre mayúsculas y minúsculas
Al explotar el poder de este paquete, puedes crear aplicaciones Go que manejen texto de manera precisa y eficiente, independientemente del idioma o script utilizado.