LO Basic. Аналог функций листа Calc CLEAN и TRIM

Автор ost, 28 июня 2021, 19:23

0 Пользователи и 1 гость просматривают эту тему.

ost

Доброго.
Подскажите, можно ли средствами LO Basic добиться функционала формулы листа Calc "=CLEAN(TRIM("Строка"))"  по-изячнее? Лишние пробелы в середине строки нужно удалять.
Спасибо.

sokol92

В LO можно смело вызывать функции рабочего листа. В отличие от Excel разница в быстродействии не так существенна (если делать правильно).

Sub testClean
  Dim s As String
  s="*A  " & Chr(10) & " B*"
  Msgbox ShFunc("Trim", Array(ShFunc("Clean", Array(s)))) 
  Msgbox ShFunc("Trim", ShFunc("Clean", s)) ' для функций с одним аргументом можно записать короче     
End Sub

' Вызывает функцию рабочего листа.
' - aName имя функции (регистронезависимо)
' - aArguments массив параметров. Если параметр один, то можно передать скалярное значение.
Function ShFunc(ByVal aName, ByVal aArguments)
  Static oFA
  If Not IsArray(aArguments) Then
    aArguments=Array(aArguments)
  End If 
  If IsEmpty(oFA) Then
    oFA = createUnoService("com.sun.star.sheet.FunctionAccess")
  End If 
  ShFunc=oFA.callFunction(aName, aArguments)
End Function
Владимир.

rami

Функция replace может заменить два пробела на один:
Sub Main
Dim s$, t$
s = "Лишние  пробелы  в середине  строки нужно удалять."
t = replace(s, "  ", " ")
msgbox(t)
End Sub

sokol92

Для удаления всех лишних пробелов (то, что делает функция рабочего листа Trim) можно использовать такой "трюк":

Sub TestTrim
Dim s
s="         *A     B     С*"
Msgbox Replace(Replace(Replace(Trim(s), " ", " " & Chr(0)), Chr(0) & " ", ""), Chr(0), "")
End Sub
Владимир.

mikekaganski

#4

Function CleanTrim(ByVal s As String) As String
 Dim ch As Integer, s1 As String
 ' Clean
 For ch = 0 To 31
   s = Replace(s, Chr(ch), "")
 Next ch
 s = Replace(s, Chr(&H7f), "")
 ' Trim
 For Each s In Split(s)
   If Len(s) > 0 Then s1 = s1 & " " & s
 Next
 CleanTrim = Trim(s1)
End Function
С уважением,
Михаил Каганский

mikekaganski

Цитата: ost от 28 июня 2021, 19:23функционала формулы листа Calc "=CLEAN(TRIM("Строка"))"  по-изячнее? Лишние пробелы в середине строки нужно удалять.

Обратите внимание, что эта формула Calc даст неожиданные результаты для строки "ab " & CHAR(9) & " cd". Порядок следования функций следовало бы обратить.
С уважением,
Михаил Каганский

sokol92

#6
Проверим быстродействие указанных подходов.
В тестах ниже CleanTrim функция из #4, CleanTrim2 - из #1, CleanTrim3 - Тянитолкай: Clean из #4 (чуть измененный), Trim из #3.

Время выполнения (сек):
CleanTrim   23.2
CleanTrim2   2.2
CleanTrim3   3.3

Для сопоставления: Excel (32) на том же компьютере:
CleanTrim     1.9
CleanTrim2    0.1
CleanTrim3    1.9

Для Excel вызов функций рабочего листа можно выполнять через объект Application непосредственно (VBA):
Application.Trim(Application.Clean(s))

Программа сравнения (Lo Basic):
Option Explicit

Sub Derby()
 Dim s As String, res As String, t As Long, nMax As Long, i As Long
 
 s="*A  " & Chr(9) & "  B* "
 
 s=ShFunc("Rept", Array(s, 100)) ' повторили 100 раз
 nMax=10000
 
 t=getSystemTicks()
 For i=1 To nMax
   res=CleanTrim(s)
 Next i
 Msgbox "CleanTrim: " & (getSystemTicks() - t)

 t=getSystemTicks()
 For i=1 To nMax
   res=CleanTrim2(s)
 Next i
 Msgbox "CleanTrim2: " & (getSystemTicks() - t)

 t=getSystemTicks()
 For i=1 To nMax
   res=CleanTrim3(s)
 Next i
 Msgbox "CleanTrim3: " & (getSystemTicks() - t)
End Sub

Function CleanTrim(ByVal s As String) As String
 Dim ch As Integer, s1 As String
 ' Clean
 For ch = 0 To 31
   s = Replace(s, Chr(ch), "")
 Next ch
 s = Replace(s, Chr(&H7f), "")
 ' Trim
 For Each s In Split(s)
   If Len(s) > 0 Then s1 = s1 & " " & s
 Next
 CleanTrim = Trim(s1)
End Function


Function CleanTrim2(ByVal s As String) As String
  CleanTrim2=ShFunc("Trim", ShFunc("Clean", s)) ' для функций с одним аргументом можно записать короче    
End Function

Function CleanTrim3(ByVal s As String) As String
 Dim ch As Integer, s1 As String
 ' Clean
 For ch = 0 To 31
   If Instr(1, s, Chr(ch), 0)>0 Then s = Replace(s, Chr(ch), "")
 Next ch
 s = Replace(s, Chr(&H7f), "")
 ' Trim
 CleanTrim3=Replace(Replace(Replace(Trim(s), " ", " " & Chr(0)), Chr(0) & " ", ""), Chr(0), "")
End Function

' Вызывает функцию рабочего листа.
' - aName имя функции (регистронезависимо)
' - aArguments массив параметров. Если параметр один, то можно передать скалярное значение.
Function ShFunc(ByVal aName, ByVal aArguments)
 Static oFA
 If Not IsArray(aArguments) Then
   aArguments=Array(aArguments)
 End If  
 If IsEmpty(oFA) Then
   oFA = createUnoService("com.sun.star.sheet.FunctionAccess")
 End If  
 ShFunc=oFA.callFunction(aName, aArguments)
End Function

Владимир.

mikekaganski

Да, очень интересная проблема производительности. Стоило бы написать баг, построить флеймграф ... ;)
С уважением,
Михаил Каганский

economist

#8
Чисто "из любви к искусству", тот же тест на Python в виде 2-х функций Basic + Python

Function CleanTrim4(s as String) as String
oScriptProvider = ThisComponent.getScriptProvider()
oScript = oScriptProvider.getScript("vnd.sun.star.script:clean.py$clean?language=Python&location=share")
CleanTrim4 = oScript.invoke(array(s), array(), array())
End Function


#clean.py file content
def clean(s):
   return " ".join(s.split())


Дает CleanTrim4: 11 сек., что в ~5 раз медленнее чистого LO Basic. Шта?
Но не будем спешить с выводами. Вызов интерпретатора из LO - затратная операция, поскольку сам Python выполняет тот же код 10k раз всего за 0.005 сек. А можно ещё быстрее?

Если использовать очистку на более жизненных примерах (например есть таблица, в ней столбец с "мусором"), то Pandas очищает 10k ячеек в столбце всего за 0.002 сек.:

d = pd.Series("*A  " + '\t' + "  B* ", index=np.arange(10000))
%%timeit
d.apply(lambda x: ' '.join(x.split()))
>1.89 ms ± 63.1 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)


Вывод: если вам нужно, условно говоря, 1000-кратное ускорение очистки строк от мусора по сравнению с LO Basic  - то используйте Pandas в LO Python, "мурзилки" по установке есть на Форуме. Очищенную таблицу легко сохранить в *.ods (и еще 14+ форматов) командой df.to_excel('D:/123.ods')
Использование прочих оптимизаций типа re.compile() - на пробельных символах выигрыша не даст, но в других ситуациях он, безусловно, будет. 
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...

sokol92

Цитата: mikekaganski от 29 июня 2021, 22:07Да, очень интересная проблема производительности.
Я (уже давно) планирую создать тему, посвященную скорости интерпретации LO Basic (по сравнению с VBA). Начнем с простейших конструкций, для которых флеймграф вряд ли нужен.  :)
Владимир.

kompilainenn

Цитата: sokol92 от 30 июня 2021, 13:17Я (уже давно) планирую создать тему, посвященную скорости интерпретации LO Basic (по сравнению с VBA).
Которая будет полезна чем?
Поддержать разработчиков LibreOffice можно тут, а наш форум вот тут

sokol92

Возможно, поймем, в каком направлении двигаться. Тогда и баги напишем.
Владимир.

kompilainenn

Цитата: sokol92 от 30 июня 2021, 13:51Возможно, поймем, в каком направлении двигаться
Я вижу выход в переводе ЛО на юзание Питона, как основного ЯП для макросов
Поддержать разработчиков LibreOffice можно тут, а наш форум вот тут

sokol92

Для обоих языков найдется своя ниша. Что касается Питона в LO, то основная, на мой взгляд, проблема - переход на полноценную версию Python (о чем коллега @economist неустанно твердит  :) ).
Владимир.