LibreOffice BASIC frente a PYTHON para LibreOffice

¿Qué lenguaje es mejor para programar las macros de LibreOffice, BASIC o PYTHON?

La respuesta, de acuerdo con mi experiencia personal y laboral, es … Depende.

Por un lado BASIC dispone de un IDE (Integrated Development Environment), entorno de desarrollo integrado en LibreOffice mientras que PYTHON no, por lo que tenemos que ayudarnos de un editor externo (personalmente utilizo Notepad++, para gustos colores como decimos en España). Además, si queremos que nuestra macro en Python vaya con el archivo Calc correspondiente debemos usar la extensión APSO - Alternative Script Organizer for Python si no queremos complicarnos la vida en exceso. En la Ayuda con las macros en Python en LibreOffice encontraremos mucha información sobre cómo usar Python en LibreOffice.

He trabajado con archivos de texto de más de 100.000 líneas con 500 caracteres por línea. Y Basic no es rival para Python. Mientras que el primero tardaba desesperantes minutos, Python lo resolvía en muy pocos segundos.

Pero, por otro lado, programar en Basic es menos “complicado”. Por eso la respuesta de “depende”.

He programado una misma macro en Basic y en Python para poder mostrar los resultados. He utilizado la biblioteca ScriptForge tanto en uno como en otro. La macro lee un archivo de texto plano (TXT - UTF-8) de 20.000 líneas y si los primeros 15 caracteres de cada línea coinciden con el patrón “expr_regular” entonces suma dos cantidades en las columnas 32 y 108 previamente convertidas a números (recordad que en Python se comienza por 0, 33 y 109 en Basic).

En la Imagen 1 vemos el resultado de Basic.

Imagen 1: Tiempo empleado (BASIC)

Basic ha ido a una velocidad de 383 líneas por segundo.

En la Imagen 2 vemos el resultado de Python.

Imagen 2: Tiempo empleado (PYTHON)

Por el contrario, la velocidad de Python ha sido de 303.030 líneas por segundo. 792 veces más rápido Python que Basic en el tratamiento de archivos TXT.

Macro en Basic:

REM  *****  BASIC  *****
' Esta macro lee un archivo TXT de 20.000 líneas y si los primeros
' 15 caracteres de cada línea coinciden con el patrón expr_regular
' entonces suma dos cantidades en las columnas 33 y 109 (recordad que
' se comienza por 1) previamente convertidas a números.

Option Explicit
' Constantes para MsgBox
Const ICONO_STOP As Integer = 16
Const ICONO_INTERROGACION As Integer = 32
Const ICONO_EXCLAMACION As Integer = 48
Const ICONO_INFORMACION As Integer = 64
Const BOTON_ACEPTAR As Integer = 0

Sub Main()
Dim sv_fso As Object
Dim archivo As Object
Dim sv_time As Object
Dim linea As String
Dim contador As Long
Dim linea_encontrada As Long
Dim suma(1) As Double
Dim expr_regular As String
Dim mensaje As String
Dim pos_ini As Integer
Dim largo_expr_regular As Integer
Dim resultado As String

'Cargar la biblioteca ScriptForge si no está ya cargada
If Not GlobalScope.BasicLibraries.islibraryloaded("ScriptForge") Then
GlobalScope.BasicLibraries.loadLibrary("ScriptForge")
End If
' Automáticamente se crea el objeto SF_String mediante el cual
' se puede llamar a todos los métodos del servicio String.
' Se cargan los servicios necesarios de la biblioteca ScriptForge.
sv_fso = CreateScriptService("FileSystem")
sv_time = CreateScriptService("Timer", True)

expr_regular = "\d\d\.\d\d\.\d\d\.\d\d\.\d\d\."
pos_ini = 1
largo_expr_regular = 15
contador = 0
linea_encontrada = 0
suma(0) = 0.00
suma(1) = 0.00

' Comienza a correr el tiempo.
sv_time.Start()
Set archivo = sv_fso.OpenTextFile("D:\ARCHIVO_DE_PRUEBAS.TXT", sv_fso.ForReading, False, "UTF-8")
Do While Not archivo.AtEndOfStream
contador = contador + 1
linea = archivo.ReadLine()
resultado = SF_String.FindRegex(Mid(linea, pos_ini, largo_expr_regular), expr_regular)
If resultado <> "" Then
linea_encontrada = linea_encontrada + 1
suma(0) = suma(0) + Val(Replace(Replace(Trim(Mid(linea, 33, 18)), ".", ""), ",", "."))
suma(1) = suma(1) + Val(Replace(Replace(Trim(Mid(linea, 109, 18)), ".", ""), ",", "."))
End If
Loop
archivo.CloseFile()
sv_time.Terminate()
' Se para el tiempo.

mensaje = "Líneas que cumplen con el patrón: " & Format(linea_encontrada, "#,##0") & _
SF_String.sfNEWLINE & SF_String.sfNEWLINE & "suma(0) = " & Format(suma(0), "#,##0.00") & _
SF_String.sfNEWLINE & "suma(1) = " & Format(suma(1), "#,##0.00") & _
SF_String.sfNEWLINE & SF_String.sfNEWLINE & _
"Tiempo: " & Format(sv_time.Duration, "0.000") & " segundos" & _
SF_String.sfNEWLINE & SF_String.sfNEWLINE & _
"Líneas leídas (según el contador del bucle):  " & Format(contador, "#,##0") & "    "
MsgBox mensaje, ICONO_INFORMACION + BOTON_ACEPTAR, "¡ATENCIÓN! ** LibreOffice BASIC **"

' Liberamos memoria.
Set sv_time = sv_time.Dispose()
Set archivo = archivo.Dispose()
Set sv_fso = sv_fso.Dispose()
End Sub
' Fin Main

Macro en Python:

''' Esta macro lee un archivo TXT de 20.000 líneas y si los primeros
    15 caracteres de cada línea coinciden con el patrón expr_regular
    entonces suma dos cantidades en las columnas 32 y 108 (recordad que
    se comienza por 0) previamente convertidas a números.
'''
# coding: utf-8
from __future__ import unicode_literals
import re
from scriptforge import CreateScriptService


def main(*args):
    # Se cargan los servicios necesarios de la biblioteca ScriptForge.
    sv_fso = CreateScriptService('FileSystem')
    sv_bas = CreateScriptService('Basic')
    sv_time = CreateScriptService('Timer', True)

    expr_regular = '\d\d\.\d\d\.\d\d\.\d\d\.\d\d\.'
    pos_ini = 0
    largo_expr_regular = 15
    contador = 0
    linea_encontrada = 0
    suma = [0.00, 0.00]

    # Comienza a correr el tiempo.
    sv_time.Start()
    with open(r'D:\ARCHIVO_DE_PRUEBAS.TXT', 'r', encoding='utf-8') as archivo:
        linea = archivo.readline()
        contador += 1
        while linea:
            if re.fullmatch(expr_regular, linea[pos_ini:pos_ini + largo_expr_regular]):
                linea_encontrada += 1
                suma[0] += float(linea[32:50].strip().replace('.', '').replace(',', '.'))
                suma[1] += float(linea[108:126].strip().replace('.', '').replace(',', '.'))
            linea = archivo.readline()
            contador += 1
    sv_time.Terminate()
    # Se para el tiempo.

    mensaje = 'Líneas que cumplen con el patrón: ' + f'{linea_encontrada:,}'.replace(',', '.') + \
            f'\n\nsuma[0] = {suma[0]:,.2f}'.replace(',', 'X').replace('.', ',').replace('X', '.') + \
            f'\nsuma[1] = {suma[1]:,.2f}'.replace(',', 'X').replace('.', ',').replace('X', '.') + \
            '\n\nTiempo: ' + f'{sv_time.Duration:.3f}'.replace('.', ',') + ' segundos' + \
            '\n\nLíneas leídas (según el contador del bucle):  ' + f'{contador:,}'.replace(',', '.') + '    '
    sv_bas.MsgBox(mensaje, sv_bas.MB_ICONINFORMATION + sv_bas.MB_OK, '¡ATENCIÓN! ** LibreOffice PYTHON **')

    # Liberamos memoria.
    sv_fso.Dispose()
    sv_time.Dispose()
    sv_bas.Dispose()
# Fin main.

Así que, dependiendo de la necesidad, obviamente, emplead BASIC o PYTHON según vuestro criterio.

Como siempre, muchas gracias por vuestra atención y saludos cordiales.

LibreOffice Community versión 25.8.4 (X86_64 es-ES) Windows 11.


Comentarios

Entradas populares de este blog

Copiar / Pegar celdas con fórmulas sin modificar las referencias relativas

Destacar valores para corregir errores

Función personalizada (UDF) para extraer los números de una cadena de texto (string)