Выделение определенного слова, состояние ячеек

Автор W1nterdreams, 21 января 2021, 00:15

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

eeigor

#15
Ну, тогда ждём ответы на поставленные здесь вопросы от других участников. Может, я чего-то не нашёл...

Цитата: Bigor от 21 января 2021, 23:17кроме InStr() я не знаю, что может его вернуть
И будет ли оно работать с регулярными выражениями?
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#16
Уже теплее... Приглядитесь. Пока не всё понятно, но аналогия с упомянутом выше кодом прослеживается.
И вот с чем надо работать:
com.sun.star.util.TextSearch Информации почти нет.
com.sun.star.util.SearchOptions

The .FindFirst finds all cells in the spreadsheet that contain SearchString. If you want to search within the string, then you need com.sun.star.util.TextSearch.
Sub getMktValue()
 Dim oDoc as Object
 Dim oSheet as Object
 Dim oCell as Object

 oDoc = ThisComponent
 oSheet = oDoc.Sheets.getByName("Income")
 'regex test code'
 oCell = oSheet.getCellByPosition(0, 1)
 stk = oCell.getString()

 oTextSearch = CreateUnoService("com.sun.star.util.TextSearch")
 oOptions = CreateUnoStruct("com.sun.star.util.SearchOptions")
 oOptions.algorithmType = com.sun.star.util.SearchAlgorithms.REGEXP
 oOptions.searchString = "\((.*)\)"
 oTextSearch.setOptions(oOptions)
 oFound = oTextSearch.searchForward(stk, 0, Len(stk))
 sFound = mid(stk, oFound.startOffset(0) + 1, oFound.endOffset(0) - oFound.startOffset(0))
 MsgBox sFound
 sFound = mid(stk, oFound.startOffset(1) + 1, oFound.endOffset(1) - oFound.startOffset(1))
 MsgBox sFound
End Sub


Да, совершенно неожиданно. UNO-сервисы и структуры вместо объектов. И самому хоть с Xray, хоть с MRI никогда не догадаться. Но у меня сейчас уже нет времени. Если кто-то реализует ту самую функцию REGEX_SELECT в первом приближении, поделитесь. Доведём до ума вместе.

https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XTextSearch.html#a8e62cf8b8b7cee9c8d2907cb4aad2fc5
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#17
Цитата: eeigor от 21 января 2021, 22:48Function REGEX_SELECT(TextRange, Pattern, Flags) As Integer
Дополнение по поводу третьего параметра Flags...
Ну, не все, может, но есть и полезные. Один: ALL_IGNORE_CASE, то бишь i.

UPD:
Я искал ещё хотя бы флаги типа: m - multiline, g - global. Первый настраивает поиск в многострочном тексте (а такие данные вполне могут быть в ячейке), а второй возвращает все совпадения, иначе - только первое. Тут не нашел. Хотя в формуле листа REGEX значится флаг g.
Собственно говоря, судя по примеру выше, мы идем по тексту ячейки по принципу функции InStr(), но с использованием regex.

UPD2:
Код выше в примере #16 работает отлично. Дело стало за малым.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

Ещё одно подтверждение правильности этого решения:
"Fine. This will often be better than to call REGEX with a FunctionAccess object. You know a way."
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#19
И если кто-то пишет REGEX_SELECT, то можно было бы и создать отдельное расширение:

REGEX_MATCH – тут
надо добавить ещё один аргумент на предпоследнюю позицию: что-то типа matchIndex,
где 0 – вся строка (default), 1..n – номер совпадения. Штатная функция этого не делает.

REGEX_SPLIT – для расщепления строки по регэкспу. Возвращает массив.

Чтобы не хуже, чем в Python... :)

UPD:
Соответственно, штатная функция не позволяет проводить замену конкретных вхождений (по индексу/номеру):
REGEX_REPLACE

Но, может, всё это и не так нужно...

Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

W1nterdreams

Оказывается, всё очень сложно...
Да, мне и правда нужно изменять шрифт не в выделенном тексте, а как бы отслеживать ввод.
Скажем в ячейке текст "всё очень сложно"
Отдельно выделить слово "всё" - красным, слово "очень" синим и слово "сложно" коричневым
В файле 2500 строк и по этим словам планируется отслеживать категорию. В writere это реализовано через простое выделение цветом указанного текста, ячейку целиком выделять - получается слишком пёстро

eeigor

#21
Это не текстовый процессор, а электронная таблица (!): нет того, что и не особенно нужно при правильно организованной работе.
Но жизнь она богаче... Мне и самому иногда надо.
Задача практически решена, то есть вся подготовительная работа проделана: осталось сесть и написать функцию (см. выше). В вашем случае чуть проще: ищем что-то одно (первое вхождение) и дополнительно задаём цвет. Но, повторяю, это не задача электронных таблиц!

UPD:
Категории отслеживают не по плохо формализованному тексту, а по значениям поля «Категория», где значение – величина атомарная, то бишь неделимая (в крайнем случае – список с разделителями). И тогда всё это будет не нужно!!
Но мы здесь решаем и немножко свои задачи, и вашу по ходу дела...
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

economist

W1nterdreams - многие хотелки так и не выбиваются в хорошие решения, если идут по порочному кругу недосказанности. Сначала вроде нужно что-то "просто выделить цветом", а потом выясняется что "зеленое" нужно просуммировать и вычесть из "красного" и поставить в отдельном листе, в виде отчета.  

Разбейте вашу строку на части - по столбцам, и вообще - изложите задачу целиком.

Что значит отслеживать? Глазами искать коричневые строки? Спецы по юзабилити вообще называют цвет - врагом точности. Это неспроста, проведены исследования, что лучше.  

Может стоит не цвет, а четкие критерии, словами, в отдельном столбце? В этом случае простая Сводная таблица на отд. листе позволит и отслеживать статусы/состояния/проблемы и будет сама же служить отчетом. Самообновляемым.
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...

eeigor

#23
Согласен с @economist. Непонятно вообще, о каких данных идёт речь. И знает ли автор о правилах нормализации данных (хотя бы трёх формах из пяти), и можно ли с имеющимися данными что-то сделать (??).
Сводная таблица – супер, но это требует избыточности в данных (таблица – записи), чего пользователи не любят... А избыточность приводит к ошибкам ввода данных. Это уже вопросы разработки приложений.
Как минимум, надо избавиться от повторяющихся групп (1 форма нормализации)


https://yandex.ru/turbo/ru.wikipedia.org/s/wiki/Нормальная_форма
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

W1nterdreams

Извините, если чем изложил задачу не так...
Задача состоит в создании таблицы для учета людей.
И проблема в том, что категорий получается слишком много, это раз, два - часть процесса так или иначе останется в ручном управлении из-за того что она тесно связана с дальнейшей обработкой информации в другом документе writera...оттого, слишком много столбцов, за которыми придется следить вручную так или иначе.
С помощью трёх слов будут отслеживаться вакантные должности, занятые должности и временно замещающие должность.
Я понимаю, что не текстовый редактор, но с учётом того, что до этого момента вся работа велась через writer  и полностью вручную, я хоть как то пытаюсь автоматизировать процесс.
Я понимаю так же, что это форум, и что вы лишь помогаете, при наличии своего времени и желания. И простите мне мою неточность в формулировке проблемы, ещё раз.
Я не знаю, принято или возможно ли на этом форме так делать, но знаю, что чужое время стоит вознаграждения, которое я готов оплатить

sokol92

#25
Можно так. Пример использования в макросе TestRangeTextsColor.

Option Compatible
' Раскрашивает различными цветами фрагменты текстов в ячейке.
' - oCell ячейка.
' - aTexts массив фрагментов текстов.
' - entireWords Если True, то раскрашивать только целые слова, иначе фрагменты.
' - aColors массив цветов (той же размерности, что aTexts).
' - matchCase False:происводить поиск слов без учета регистра букв, True - с учетом регистра.
'
Sub CellTextsColor(ByVal oCell, ByVal aTexts, ByVal aColors, Optional Byval entireWords as Boolean, Optional ByVal matchCase As Boolean)
  Dim oTextCursor, i As Long, j as Long, j0 as Long, i0 as String, s As String, bFound As Boolean
 
  If IsMissing(matchCase) Then matchCase=False
  If IsMissing(entireWords) Then entireWords=False
 
  oTextCursor=oCell.createTextCursor()
  s=oCell.String
 
  For i=LBound(aTexts) To UBound(aTexts)
    j0=1
    Do While j0<=len(s)
      If matchCase Then
        j=Instr(j0, s, aTexts(i), 0)
      Else
        j=Instr(j0, lcase(s), lcase(aTexts(i)), 0) 
      End If
       
      If j>0 Then
        bFound=True
        If entireWords Then
          If j>1 Then 
            If Ucase(Mid(s, j-1, 1))<>lCase(Mid(s, j-1, 1)) Then bFound=False   ' слева от найденного текста - буква
          End If
          If j+Len(aTexts(i))<=Len(s) Then 
            If Ucase(Mid(s, j+Len(aTexts(i)), 1))<>lCase(Mid(s, j+Len(aTexts(i)), 1)) Then bFound=False   ' справа от найденного текста - буква
          End If
        End If
        If bFound Then
          With oTextCursor
            .gotoStart False
            .goRight j-1 , False 
            .goRight Len(aTexts(i)), True
            .CharColor=aColors(i)
          End With 
        End If
      Else
        Exit Do
      End If
     
      j0=j+Len(aTexts(i))
    Loop
  Next i
End Sub

' Раскрашивает различными цветами фрагменты текстов в диапазоне(диапазонах) ячеек.
' - oRange ячейка или диапазон ячеек
' - aTexts массив фрагментов текстов.
' - entireWords Если True, то раскрашивать только целые слова, иначе фрагменты.
' - aColors массив цветов (той же размерности, что aTexts).
' - matchCase False:происводить поиск слов без учета регистра букв, True - с учетом регистра.
'
Sub RangeTextsColor(ByVal oRange, ByVal aTexts, ByVal aColors, Optional Byval entireWords as Boolean, Optional ByVal matchCase As Boolean)
  Dim oCell
  If IsMissing(matchCase) Then matchCase=False
  If IsMissing(entireWords) Then entireWords=False
 
  If oRange.supportsService("com.sun.star.sheet.SheetCell") Then
    CellTextsColor oRange, aTexts, aColors, entireWords, matchCase
  Else
    For Each oCell In oRange.queryContentCells(com.sun.star.sheet.CellFlags.STRING).getCells
      CellTextsColor oCell, aTexts, aColors, entireWords, matchCase
    Next oCell 
  End If
End Sub

' Раскрашивает в выделенном фрагменте ячеек слова "все", "очень", "сложно".
Sub TestRangeTextsColor
   RangeTextsColor ThisComponent.CurrentSelection, Array("все", "очень", "сложно"), Array(RGB(255, 0, 0), RGB(0, 255, 0), RGB(0, 0, 255)),False
End Sub

Владимир.

eeigor

#26
Узнаю sokol92 уже по стилю.  :)
Кстати ваше решение по заполнению пустых, ранее выделенных произвольных ячеек, мне помогло, и я его использую. Спасибо.
Здесь же замечу одно. Реализован более «процедурный» способ поиска вхождений функцией InStr().
Я «настаивал» на использовании чего-то пошустрее... и нашёл.
Полагаю функция REGEX, диалог «Найти и заменить» с использованием регулярных выражений используют именно его (см. пример выше с TextSearch). Этот объект работает с подстроками, которые находит все сразу, а не в цикле по одной, и позволяет обращаться к ним по индексу.

oFound.startOffset(1)  'это не 1 символ, на который надо сместится (попробуй догадайся!), а номер вхождения (match index), и в примере выше ищется текст, заключённый в скобки, и он выводится в скобках, а затем выводится то, что захвачено в группу и имеет индекс 1. В тексте регэкспа мы бы сослались на эту группу так: \1
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

sokol92

Я пользуюсь сервисом TextSearch2. В данном случае Instr вполне достаточно.
Владимир.

W1nterdreams

Спасибо, в принципе, практически всё что нужно он делает.
Можно ли его как нибудь привязать к моменту окончания редактирования ячейки. Я попробовал через привязку к событию это сделать, но там нет такого момента(

eeigor

#29
Да, можно. Событие листа «Содержание изменено» (или как-то так), щелчок правой кнопкой мыши по ярлыку листа, события... 
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community