Autonomía digital y tecnológica

Código e ideas para una internet distribuida

Expresiones regulares de vi y Perl para sustituir comas por puntos en números decimales, y cómo aplicarlo en PHP

Imago voragine.net

Los lenguajes de programación fueron inventados por gente que hablaba y escribía en inglés, así que están pensados para funcionar con las convenciones lingüisticas y matemáticas inglesas. Cuando cualquier lenguaje de programación tiene que relacionarse con bases de datos en idiomas diferentes siempre hay problemas.

Uno típico se da cuando se utiliza una base de datos en español que contiene números racionales en su expresión decimal. Para notar los decimales en inglés se utiliza el punto (0.45) y en español la coma (0,45). Así que antes de operar con estos números hay que convertir esas comas en puntos para que el lenguaje de programación entienda los números como decimales.

Este problema lo encontramos por ejemplo cuando queremos insertar datos que tenemos en un CSV a una base de datos SQL, utilizando PHP. Pondré como ejemplo el CSV de un proyecto en el que estoy trabajando, una herramienta para medir las emisiones de dióxido de carbono en los procesos constructivos:

CÓDIGO,UNIDAD,"MATERIAL BÁSICO","MATERIAL 1","MATERIAL 2","MATERIAL 3",Masa,Masa,Masa,"TOTAL KG","FACTOR DE CALCULO HCE"
P01AJM086,u,"Perlita (envasado en 125 litros)","PERLITA EXPANDIDA",,,"67,125",,,"67,125",0
P01BC010,u,"Bloq.horm. celular 62,5x25x7","HORMIGON CELULAR",,,"6,015625",,,"6,015625",0
P01BC020,u,"Bloq.horm. celular 62,5x50x7","HORMIGON CELULAR",,,"12,03125",,,"12,03125",0
P01BC030,u,"Bloq.horm. celular 62,5x75x7","HORMIGON CELULAR",,,"18,046875",,,"18,046875",0
P01BC040,u,"Bloq.horm. celular 62,5x25x10","HORMIGON CELULAR",,,"8,59375",,,"8,59375",0
P01BC050,u,"Bloq.horm. celular 62,5x50x10","HORMIGON CELULAR",,,"17,1875",,,"17,1875",0
P01BC060,u,"Bloq.horm. celular 62,5x75x10","HORMIGON CELULAR",,,"25,78125",,,"25,78125",0
P01BC070,u,"Bloq.horm. celular 62,5x25x12,5","HORMIGON CELULAR",,,"10,7421875",,,"10,7421875",0

En este caso, nos interesa realizar la sustitución en las columnas 7 y 10, Masa y «TOTAL KG».

Podemos transformar los números fácilmente usando expresiones regulares de vi en el editor vim, o expresiones regulares Perl en PHP mediante la función preg_replace.

Sustitución en editor vim mediante expresiones regulares vi

En vim sencillamente podemos realizar una búsqueda con sustitución en todas las líneas del archivo CSV:

:1,$s/"\(\d\{1,}\),\(\d\{1,}\)"/"\1.\2"/g

Sustitución en PHP mediante la función preg_replace y expresiones regulares compatibles con PERL

En PHP tenemos que abrir el archivo CSV y montar un loop para recorrer el archivo línea a línea buscando decimales y sustituyendo comas por puntos únicamente en los casos adecuados. Aunque es un procedimiento más largo, tiene la ventaja de que podemos seleccionar las columnas en las que queremos realizar la sustitución.

// datos del archivo CSV
$filename = "archivo.csv"; // rura relativa hasta el archivo
$line_length = "1024"; // extensión máxima de línea (para líneas no muy largas debería bastar con 1024 caracteres)
$delimiter = ","; // caracter separador de campos
$enclosure = '"'; // caracter delimitador de campos

// abrimos el archivo únicamente con permisos de lectura, no necesitamos más
$fp = fopen($filename,'r');

if ( $fp !== FALSE ) { // si el archivo existe y tiene permisos de lectura
   $line = 0;
   $pattern = '/(\d+),(\d+)/';
   $replacement = '$1.$2';
   while ( ($fp_csv = fgetcsv($fp,$line_length,$delimiter,$enclosure)) !== FALSE ) { // comenzamos el loop
      if ( $line == 0 ) { /* saltamos la primera línea, que contiene la cabecera del archivo */ }
      else {
        foreach ( array(6,9) as $colum ) { // hacemos la sustitución en las columnas 7 y 10 (la primera es la 0)
          $string = $fp_csv[$colum];
          $new_string = preg_replace($pattern,$replacement,$string);

          // generamos un output mínimo para ver si está funcionando
          echo "Nuevo valor de la columna ".$colum+1. ": <strong>".$new_string."</strong><br>";

        } // end foreach loop columnas

      } // end if primera línea
      $line++;
      
   } // end while loop
   fclose($fp);

} else { // if archivo no existe
   // Mensaje de error

"; } // end if archivo existe

El código PHP anterior debería generar un output como el siguiente:

Nuevo valor de la columna 7: 67.125
Nuevo valor de la columna 10: 67.125
Nuevo valor de la columna 7: 6.015625
Nuevo valor de la columna 10: 6.015625
Nuevo valor de la columna 7: 12.03125
Nuevo valor de la columna 10: 12.03125
Nuevo valor de la columna 7: 18.046875
Nuevo valor de la columna 10: 18.046875
Nuevo valor de la columna 7: 8.59375
Nuevo valor de la columna 10: 8.59375
Nuevo valor de la columna 7: 17.1875
Nuevo valor de la columna 10: 17.1875
Nuevo valor de la columna 7: 25.78125
Nuevo valor de la columna 10: 25.78125
Nuevo valor de la columna 7: 10.7421875
Nuevo valor de la columna 10: 10.7421875

2 comentarios

    • Por Ale

    Hola Alfonso!
    Te quería apuntar que la expresión de vim la puedes hacer algo más sintética:
    :%s/\(\d\+\),\(\d\+\)/\1.\2
    Me gusta tu blog. Un saludo!

    1. Pues sí, ¡cierto! Muchas gracias Ale.

      Un placer leerte por aquí :)

Dejar un comentario

*
*

 

No hay trackbacks