Paquete strings en Go
El paquete strings ofrece herramientas completas para manipular y analizar cadenas en Go. Esta guía ampliada incluye ejemplos detallados, mejores prácticas y casos de uso avanzados.
1. Funciones Esenciales
1.1 Búsqueda y Comparación
Funciones clave:
package main
import (
"fmt"
"strings"
)
func main() {
s := "Go es rápido y divertido 🔥"
// Contains vs ContainsAny
fmt.Println("Contiene 'rápido':", strings.Contains(s, "rápido")) // true
fmt.Println("Contiene 'aeiou':", strings.ContainsAny(s, "aeiou")) // true (busca cualquier carácter)
// Index y LastIndex con Unicode
fmt.Println("Índice de 'es':", strings.Index(s, "es")) // 3
fmt.Println("Última 'o':", strings.LastIndex(s, "o")) // 1
// Comparación exacta vs insensible a mayúsculas
fmt.Println("Igualdad exacta:", strings.Compare("Go", "go")) // -1
fmt.Println("Igualdad insensible:", strings.EqualFold("Go", "go")) // true
}
1.2 División y Unión
Ejemplos con manejo de slices:
func main() {
// Split con múltiples separadores
csv := "a,b;c|d"
parts := strings.FieldsFunc(csv, func(r rune) bool {
return r == ',' || r == ';' || r == '|'
})
fmt.Println("Split personalizado:", parts) // [a b c d]
// Unión eficiente
palabras := []string{"Go", "es", "genial"}
fmt.Println("Unido:", strings.Join(palabras, "💡")) // Go💡es💡genial
// Repetición de cadenas
fmt.Println("Ritmo:", strings.Repeat("🎵", 3)) // 🎵🎵🎵
}
2. Transformaciones Avanzadas
2.1 Manipulación de Caracteres
Uso de strings.Map y strings.ToValidUTF8:
func main() {
// Eliminar dígitos de una cadena
limpio := strings.Map(func(r rune) rune {
if r >= '0' && r <= '9' {
return -1 // Eliminar
}
return r
}, "H0l4 MUnd0")
fmt.Println("Sin dígitos:", limpio) // Hl MUnd
// Sanitizar UTF-8
corrupto := "Hola\x80Mundo"
seguro := strings.ToValidUTF8(corrupto, "")
fmt.Println("Sanitizado:", seguro) // HolaMundo
}
2.2 Recortes Específicos
Ejemplos con TrimPrefix, TrimSuffix y TrimFunc:
func main() {
// Eliminar prefijos/sufijos
path := "/home/usuario/archivo.txt"
fmt.Println("Sin prefijo:", strings.TrimPrefix(path, "/home/")) // usuario/archivo.txt
fmt.Println("Sin extensión:", strings.TrimSuffix(path, ".txt")) // /home/usuario/archivo
// Recortar usando lógica personalizada
s := "!!!¡Alerta!!!¡¡"
trimmed := strings.TrimFunc(s, func(r rune) bool {
return r == '!' || r == '¡'
})
fmt.Println("Recortado:", trimmed) // Alerta
}
3. Casos de Uso Profesionales
3.1 Procesamiento de CSV
Parseo seguro con manejo de comillas:
func parseCSV(csvData string) [][]string {
reader := strings.NewReader(csvData)
var result [][]string
for {
line, err := reader.ReadString('\n')
if err != nil {
break
}
line = strings.TrimSpace(line)
fields := strings.Split(line, ",")
// Manejar comillas dobles
for i, field := range fields {
fields[i] = strings.Trim(field, `"`)
}
result = append(result, fields)
}
return result
}
3.2 Validación de Correo Electrónico
Implementación básica:
func esEmailValido(email string) bool {
// Verificar estructura básica
if !strings.Contains(email, "@") || strings.Count(email, "@") > 1 {
return false
}
partes := strings.Split(email, "@")
local, dominio := partes[0], partes[1]
// Longitudes mínimas
if len(local) < 1 || len(dominio) < 3 {
return false
}
// Caracteres prohibidos en dominio
return !strings.ContainsAny(dominio, "!#$%^&*()")
}
4. Rendimiento y Optimización
4.1 Benchmarks de Concatenación
Resultados típicos:
| Método | 1000 iteraciones |
|---|---|
+ |
1500 µs |
strings.Join |
200 µs |
strings.Builder |
50 µs |
Código recomendado:
func concatenarEficiente(palabras []string) string {
var sb strings.Builder
sb.Grow(1000) // Pre-asignar capacidad
for _, p := range palabras {
sb.WriteString(p)
}
return sb.String()
}
4.2 Manejo de Slices
Peligro de aliasing:
func main() {
s := "uno,dos,tres"
partes := strings.Split(s, ",")
partes[0] = "cero"
fmt.Println(s) // "uno,dos,tres" (el original no cambia)
}
5. Errores Comunes
5.1 Índices y Subcadenas
Ejemplo problemático:
func main() {
s := "café"
fmt.Println("Longitud incorrecta:", len(s)) // 5 (bytes), no 4 (runas)
fmt.Println("Subcadena rota:", s[0:3]) // "caf" incompleto
}
// Solución:
func subcadenaSegura(s string, inicio, fin int) string {
runas := []rune(s)
if inicio < 0 || fin > len(runas) {
return ""
}
return string(runas[inicio:fin])
}
5.2 Trim vs Replace
Confusión frecuente:
// TrimSpace solo elimina espacios al inicio/final
fmt.Println(strings.TrimSpace(" hola\t\n")) // "hola"
// Replace elimina todos los espacios
fmt.Println(strings.ReplaceAll(" hola\t\n", " ", "")) // No maneja tabs ni newlines
6. Ejercicios Prácticos
-
Inversión de Cadena:
func invertir(s string) string { runas := []rune(s) for i, j := 0, len(runas)-1; i < j; i, j = i+1, j-1 { runas[i], runas[j] = runas[j], runas[i] } return string(runas) } -
Contador de Palabras Únicas:
func contarUnicas(s string) int { palabras := strings.Fields(s) unicas := make(map[string]bool) for _, p := range palabras { unicas[strings.ToLower(p)] = true } return len(unicas) }
7. Mejores Prácticas
- Unicode: Usar
[]runepara manipulación de caracteres multibyte. - Pre-asignación: Utilizar
Grow()enstrings.Builderpara reducir asignaciones. - Seguridad: Sanitizar entradas con
strings.ToValidUTF8ystrings.TrimSpace. - Testing: Validar edge cases como cadenas vacías, UTF-8 complejo y caracteres de control.
Conclusión
El paquete strings es una herramienta poderosa para cualquier desarrollador Go. Al dominar sus funciones y aplicar estas prácticas, podrás escribir código más eficiente, seguro y mantenible.