Dominio de Strings y Transformación de Texto en Dart

En Dart, un String es una secuencia inmutable de unidades de código UTF-16. Aunque la interpolación básica con ${} es suficiente para tareas simples, el manejo de datos complejos requiere entender cómo el analizador léxico interpreta los literales y cómo podemos transformar secuencias de caracteres de forma programática.

Para entender esto, hay que diferenciar entre cómo el compilador procesa el texto. Un string normal interpreta secuencias de escape como \n (salto de línea) o \t (tabulación). Sin embargo, cuando utilizas raw strings (prefijo r), el compilador ignora estas secuencias y las trata como texto literal; esto es fundamental cuando trabajas con expresiones regulares o rutas de archivos en sistemas que usan backslash, como Windows. Por otro lado, los strings multilínea (triple comilla ''') permiten definir bloques de texto que mantienen los saltos de línea, lo cual es ideal para fragmentos de SQL o documentos embebidos, pero con una trampa: preservan la indentación del código fuente, lo que puede incluir espacios indeseados en el valor final.

Si necesitas construir texto dinámicamente desde una lista de valores numéricos (code points), String.fromCharCodes es la herramienta de bajo nivel necesaria. Para transformaciones donde el reemplazo no es un valor estático, sino que depende de lo que se encontró, replaceAllMapped es superior a un simple replaceAll, ya que te permite ejecutar una función callback por cada coincidencia. Finalmente, recuerda que la interpolación tiene límites de legibilidad: intentar meter lógica de control (como un if complejo) dentro de un ${} hará que el código sea casi imposible de mantener; la regla de oro es extraer esa lógica a una variable o método antes de integrarla en el string.

void main() {
  // 1. Raw strings para Regex y rutas (evita el "backslash hell")
  final path = r'C:\Users\Dev\project';
  final regexPattern = r'\d{3}-\d{4}';

  // 2. Multilínea con limpieza de indentación
  // Usamos trimLeft() para eliminar el salto inicial y el margen izquierdo 
  // provocado por la indentación del bloque de código.
  final sqlQuery = '''
    SELECT id, name 
    FROM users 
    WHERE status = 'active'
    LIMIT 10;
  '''.trimLeft();

  // 3. Transformación compleja con replaceAllMapped
  // Imagina que parseamos un log donde los IDs vienen en un formato sucio.
  final logEntry = "LOG: [ID:101] ERROR: [ID:102] INFO: [ID:103]";
  final cleanedLog = logEntry.replaceAllMapped(
    RegExp(r'\[ID:(\d+)\]'), 
    (Match match) {
      // match.group(1) nos da el contenido capturado por el paréntesis en la Regex
      final id = match.group(1);
      return 'ID_$id'; // Transformación dinámica
    },
  );

  // 4. Construcción desde code points y transformaciones de lista
  final headerChars = [72, 69, 78]; // H, E, N
  final header = String.fromCharCodes(headerChars);

  final tags = "dart,backend,runtime";
  final formattedTags = tags
      .split(',')
      .map((s) => s.toUpperCase())
      .join(' | ');

  // Salida consolidada
  print('$header | Processed Log: $cleanedLog');
  print('Path: $path | Pattern: $regexPattern');
  print('Query:\n$sqlQuery');
  print('Tags: $formattedTags');
}

Desglose del código

En el ejemplo anterior, observa cómo regexPattern utiliza un raw string (r''). Si no usáramos r, tendríamos que escribir \\d para que la barra invertida no se interprete como un escape de Dart, lo que duplica la carga cognitiva.

Al definir sqlQuery con triple comilla, el compilador mantiene el formato para que el código sea legible, pero debido a que el bloque está dentro de una función, la indentación del código se convierte en parte del string. La llamada a .trimLeft() es la solución estándar para limpiar ese margen inicial sin sacrificar la estructura del texto.

La potencia de replaceAllMapped se ve en cleanedLog. A diferencia de replaceAll, que solo busca y sustituye, replaceAllMapped pasa un objeto Match a la función anónima. Esto permite extraer grupos específicos mediante match.group(1), permitiendo que el reemplazo sea el resultado de una lógica de programación y no solo una sustitución de texto plano.

En cuanto a String.fromCharCodes(headerChars), estamos saltando la capa de sintaxis de literales para trabajar directamente con la representación en memoria de los caracteres, algo vital cuando recibes datos binarios de un socket o un archivo. Finalmente, la cadena de métodos split().map().join() es el patrón idiomático en Dart para procesar colecciones de texto de forma funcional, evitando bucles for manuales que ensucian el código.

El error frecuente

Un error común al usar strings multilínea es olvidar que la indentación del código fuente es parte integrante del contenido.

void printError() {
  // Error: El string contendrá los espacios de la indentación del método
  const badString = '''
    Hola mundo
    Este es un error de margen''';
  
  print(badString);
}

Si intentas comparar badString con un string que no tiene esos espacios, la comparación fallará, aunque visualmente parezcan iguales en tu editor. Siempre utiliza .trim() si quieres limpiar los extremos, o asegúrate de que el contenido empiece en la columna 0 del archivo si la indentación es crítica para la lógica de tu programa.

116

Dejar un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Scroll al inicio