Строку в массив

Автор TanaTiX, 9 марта 2022, 12:04

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

mikekaganski

Цитата: eeigor от  9 марта 2022, 20:09
A1: =REGEX("111;;222;333;555";"[^;]+";;ROW())
даст нужный результат.

Нет. Если Вы посмотрите багрепорт, Вы увидите, что я там специально привёл строку с двумя идущими подряд разделителями. Пустая строка - допустимый результат; проблема в том, что в настоящий момент кроме правильных, возвращаются ещё и некорректные пустые строки.

Хотя, возможно, Вы имеете ввиду конкретно строку из стартового сообщения - тогда да, там плюс будет работать.

Цитата: eeigor от  9 марта 2022, 20:09А вот метод searchForward все пустые regex-строки "добросовестно" пропустит. Как же так? В смысле: должно быть "безобразно, но однообразно".
Кстати, интересно.
С уважением,
Михаил Каганский

Tigrik

Владимир, Благодарю.
Что-то подобное я уже скачивал с этого форума (и, скорее всего, от Вас), но оно затерялось на моём компе.

eeigor

#17
Что ж, загружу и свой вариант...
Чуть больше (509)
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#18
Цитата: mikekaganski от  9 марта 2022, 20:17Нет. Если Вы посмотрите багрепорт, Вы увидите, что...
Михаил, regex в Вашем примере - всё правильно. Это не баг, это что-то другое (Смена "парадигмы"? Кто-то "поборолся" за чистоту вопроса? :)).
Если я не ошибаюсь, то раньше функция REGEX также "добросовестно" пропускала пустые строки, как и метод, например, searchForward. Что-то случилось, и теперь вот так (см. мой ответ #13). Трудно представить ситуацию, когда нам понадобились бы пустые строки. Вероятно, по этой причине они при создании функции были исключены из коллекции вхождений (match collection).
Я с регулярными выражениями познакомился ранее, использовал их в Excel, и там все пустые строки там были моими. Здесь сначала привыкал, что я избавлен от пустых строк... и привык.

См. https://regex101.com/

REGEX EXPRESSION
[^;]*

TEST STRING
111;;222;333;555

MATCH INFORMATION
0-3   111
3-3   null
4-4   null
5-8   222
8-8   null
9-12   333
12-12   null
13-16   555
16-16   null

Вот эти "красненькие" столбики и есть пустые строки из-за квантификатора * (* matches the previous token between zero and unlimited times).
"So there are unexpected empty matches *after* each correct non-empty match". Before.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

mikekaganski

#19
Цитата: eeigor от  9 марта 2022, 21:15
Михаил, regex в Вашем примере - всё правильно.

Нет, это не правильно. Если Вы получили первый  match для строки 111;;222;333;555 и регулярки [^;]*, то этот match включает всё до первой точки с запятой. Не осталось там ничего между первым вхождением и точкой с запятой, потому что * "жадная", и соответственно продолжение поиска идёт с исключением задней границы точки с запятой. Иначе почему только одна пустая строка между 111 и ;? Ведь после получения этой пустой строки мы всё ещё перед точкой с запятой, мы её не пересекли! Давайте снова её найдём. И ещё. И если эта пустая строка - валидный результат между первым 111 и точкой с запятой, то почему этот результат не присоединён к жадной звёздочке?

Короче, то, что Вы видите такой же результат на сервисе, использующем ту же самую библиотеку ICU, абсолютно ничего не обосновывает.

Цитата: eeigor от  9 марта 2022, 21:15
Вот эти "красненькие" столбики и есть пустые строки из-за квантификатора * (* matches the previous token between zero and unlimited times).
"So there are unexpected empty matches *after* each correct non-empty match". Before.

Нет, after. После найденного жадным алгоритмом вхождения. А то, что оно перед разделителем - не существенно для того, о чём я пишу.
С уважением,
Михаил Каганский

eeigor

#20
Попробовал здесь (менее удобно): https://regexr.com/
То же самое.

Цитата: mikekaganski от  9 марта 2022, 22:36потому что * "жадная"
В режиме non-greedy происходит совпадение со всеми пустыми строками, иначе 5.
См. https://regex101.com/

REGEX EXPRESSION
Non-greedy version:
[^;]*?

TEST STRING
111;;222;333;555

0-0   null
0-1   1
1-1   null
1-2   1
2-2   null
2-3   1
3-3   null
4-4   null
5-5   null
5-6   2
6-6   null
6-7   2
7-7   null
7-8   2
8-8   null
9-9   null
9-10   3
10-10   null
10-11   3
11-11   null
11-12   3
12-12   null
13-13   null
13-14   5
14-14   null
14-15   5
15-15   null
15-16   5
16-16   null

Опять-таки, если верить сервису...
Михаил, мне понятна логика Вашего объяснения, и я с ней как бы согласен, но...

* matches the previous token between zero and unlimited times, as many times as possible, giving back as needed (greedy)
Цитата: mikekaganski от  9 марта 2022, 22:36потому что * "жадная", и соответственно продолжение поиска идёт с исключением задней границы точки с запятой
Да. Но когда мы нашли первое вхождение (до первой точки с запятой), то второй поиск найдёт пустую строку, даже не сдвинувшись с места, и т. д. После этого поиск шагнёт за ";" и снова найдёт пустую строку. И далее 3 раза найдёт пустую строку по той же причине: не сдвинувшись с места.
Цитата: mikekaganski от  9 марта 2022, 22:36Ведь после получения этой пустой строки мы всё ещё перед точкой с запятой, мы её не пересекли! Давайте снова её найдём.
Да, мы входим в бесконечный цикл. Но regex-движок, насколько я могу судить, справляется с этой ситуацией и делает шаг вперёд, не повторяя найденное более одного раза. Вот этот единственный match Вы отвергаете. И он нам действительно не нужен, как я уже говорил, хотя это и не отменяет его существование. С другой стороны, непонятно, почему пустая строка не съедается вместе с цифрами... Что говорит спецификация языка Perl?

Впрочем, я не знаю, как к этому "относятся" другие движки со своими диалектами (flavor). Calc может поступить по-своему. Хотя мы уже согласились, что этот "мусор" нам не нужен в принципе.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

mikekaganski

С уважением,
Михаил Каганский

eeigor

#22
Михаил, спасибо. Автор этого сайта есть самый большой специалист в этом вопросе. Его программа самая-самая и платная.
Велико разнообразие.
Впечатление такое, что изменено поведение ф-ии REGEX, когда пустые строки не пропускаются по умолчанию, как это было ранее.

Метод searchForward (см. выше) их пропускал точно, но в версии 7.3 я не проверял.

Edit:
Михаил, кмк, утверждение, что жадный поиск съедает пустую строку после группы цифр (в нашем примере) перед точкой с запятой, неверно. Это не символ, и съеденная пустая строка осталась перед группой цифр согласно квантору *. Поэтому поиск верен. Другое дело, что нам это не нужно, и надо изменить поведение функции.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

mikekaganski

#23
Цитата: eeigor от 10 марта 2022, 08:33утверждение, что жадный поиск съедает пустую строку после группы цифр (в нашем примере) перед точкой с запятой, неверно. Это не символ, и съеденная пустая строка осталась перед группой цифр согласно квантору *. Поэтому поиск верен.

Ещё раз: неважно, символ это или нет. Как эту сущность ни назови, внутренне, логически неверна сама концепция, что после нахождения какого-либо совпадения с помощью жадного оператора возможно нахождение ещё одного вхождения в этом же диапазоне. Это логическое противоречие: жадный оператор обязан найти всё, после него в этом месте не имеет права быть найдено ничего.

То, что библиотеки это допускают, это лишь некая условность. При этом те же библиотеки допускают другие проблемы. Регулярки слишком мощны концептуально, и нет идеальных библиотек. Кроме того, есть соображения совместимости.
С уважением,
Михаил Каганский

eeigor

#24
Михаил, с квантором "*" должна быть найдена (применительно к нашему примеру) одна пустая строка между двумя подряд точками с запятой? Остальные "съедаются"?
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

mikekaganski

#25
Цитата: eeigor от 11 марта 2022, 19:09
Михаил, с квантором "*" должны быть найдены (применительно к нашему примеру) все пустые строки перед каждой группой цифр: после "^" и ";" ?
Но почему не была найдена пустая строка в начале строки?

Я не понял вопроса. Я, наоборот, утверждаю, что в случае регекса "[^;]*" применительно к строке "111;;222;333;555" результат должен включать только одну пустую строку - после 111 перед 222.

Последовательность шагов:
0. Разбиваем строку "111;;222;333;555" на токены. | значит токен "пустая строка": "|1|1|1|;|;|2|2|2|;|3|3|3|;|5|5|5|". Обратите внимание: токены пустых строк есть между всеми исходными символами.
1. Находим пустую строку перед 111. -> "|"
2. Поскольку оператор * жадный, проверяем, находим следующий токен - это 1. Он подходит - добавляем его к первому результату. -> "|1"
3. Находим пустую строку между первой и 2 единицами. Поскольку оператор * жадный, и найденная пустая строка подходит - добавляем её к первому результату. -> "|1|"
4. Находим вторую единицу -> "|1|1"
5. Находим третью пустую строку -> "|1|1|"
6. Находим третью единицу -> "|1|1|1"
7. Находим четвёртую пустую строку -> "|1|1|1|"
8. Находим первую точку с запятой - прерываем первый результат (наш следующий токен - это точка с запятой, а не пустая строка перед ней!).
9. Пропускаем неподходящий следующий токен ";".
10. Находим пустую строку после первой точки с запятой - добавляем ко второму результату -> "|"
11. Находим вторую точку с запятой - прерываем второй результат (наш следующий токен - это точка с запятой, а не пустая строка перед ней!)
12. Пропускаем неподходящий следующий токен ";".
13. Находим пустую строку - добавляем к третьему результату -> "|"
...

После работы алгоритма получаются следующие результаты (обозначены квадратными скобками):

"[|1|1|1|];[|];[|2|2|2|];[|3|3|3|];[|5|5|5|]"

Обратите внимание: нет ни одной неоднозначности. Работа оператора вполне определена, логична, соответствует определению "жадного" алгоритма и не даёт ни одного неожиданного лишнего результата.

EDIT: А, с правкой Ваш вопрос понятен. Да, как я показал выше, единственная пустая строка среди результатов.
С уважением,
Михаил Каганский

eeigor

#26
Да, спасибо. Я изменил на ходу вопрос (с телефона в дороге), и в новой редакции он соответствует Вашему ответу.
Я перечитал всё на эту тему. Однако проще, как раньше было, все пустые строки пропускать. Имеет смысл одна пустая строка - это: "^$" (для пустых абзацев).
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

TanaTiX

Конкретно в моем случае пустая строка (одна по примеру) может иметь значение.

eeigor

#28
Цитата: mikekaganski от  9 марта 2022, 20:17Цитата: eeigor от  9 Март 2022, 20:09
А вот метод searchForward все пустые regex-строки "добросовестно" пропустит. Как же так? В смысле: должно быть "безобразно, но однообразно".
Кстати, интересно.
Михаил, я проверил. Применительно к нашему примеру возвращено 4 вхождения (вызовом метода searchForward в цикле по строке), и никаких пустых строк (пустая строка между 2-мя точками с запятой тоже пропущена). Таким образом, в текущей версии (LO 7.3) поведение функции REGEX по умолчанию было действительно изменено. Возможно, не намеренно.

REGEX EXPRESSION
[^;]*

TEST STRING: sText
111;;222;333;555

Demo code (см. также ответ #13: инициализация объектов поиска текста с использованием regex)
   aSearchResult = oTextSearch.searchForward(sText, 0, Len(sText))
   Do While aSearchResult.subRegExpressions > 0
       With aSearchResult
           Dim s$  'string that was found
           s = Mid(sText, .startOffset(0) + 1, .endOffset(0) - .startOffset(0))
           Print s; .subRegExpressions; .startoffset(0); .endOffset(0)  'full match only

           aSearchResult = oTextSearch.searchForward(sText, .endOffset(0), Len(sText))
       End With
   Loop



Edit 1:
Что касается обработки пустых строк функцией REGEX, то, возможно, она и раньше обрабатывала их "неверно" (возвращала как в ответе #18), но был задан режим по умолчанию что-то типа NOTEMPTY, и мы не видели проблемы. Скорее всего...


Edit 2:
Цитата: mikekaganski от 10 марта 2022, 09:38...логически неверна сама концепция, что после нахождения какого-либо совпадения с помощью жадного оператора возможно нахождение ещё одного вхождения в этом же диапазоне. Это логическое противоречие: жадный оператор обязан найти всё, после него в этом месте не имеет права быть найдено ничего.
Есть всё-таки проблема с правой границей диапазона: она будет измеряться не числом (длиной), а длиной плюс возможное "ничего".  :)
Вероятно, для такого состояния требуется новой свойство, которого в regex-библиотеках нет.

Просмотрел ещё раз опубликованный Вами багрепорт. Eike Rathke фактически повторил мои "соображения" по поводу обработки пустых строк, сославшись на распространённую практику. Увы, "current behavior is predominant". Однако я уже успел перейти на Вашу точку зрения в этом вопросе. Ваши доводы логичны. Не знаю, насколько это сложно реализовать технически, ведь квантор * говорит о том , чтобы начинать поиск с каждой текущей позиции, и в случае "зацикливания" движок делает шаг вперёд (переходит к следующей позиции), однако применительно к нашему примеру перед точкой запятой возвращается пустая строка, как бы уже "съеденная" в ходе предыдущего поиска. Значит нужно с каждым новым поиском учитывать не только начальную позицию, но и результат предыдущего поиска: ведь "съедается" не символ, а пустая строка, а конечная позиция предыдущего поиска является начальной для следующего (остаётся прежней). То есть фактически надо изменить поведение квантора *, подменив его квантором +, а при необходимости восстановить *.
Возможно, текущая реализация и допускает ту "некую условность" (с возвратом лишних пустых строк), чтобы не усложнять работу движка.
Надо просто изменить работу функции REGEX по умолчанию. Большинство библиотек имеет опцию пропуска пустых строк, а некоторые используют её по умолчанию.

Ниже на скриншоте показано, как отработал поиск упоминаемый выше метод searchForward. Это и есть желаемый результат для функции REGEX. Я добавил свой комментарий в багрепорт.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community

eeigor

#29
Ситуация с пустым regex'ом.

Выскочил небольшой косяк (возможно, мой):
для пустой ячейки, когда Len(sText) = 0
метод searchForward вернул таки одно найденное значение с начальной и конечной позицией: 0-0.
aSearchResult = oTextSearch.searchForward(sText, 0, Len(sText))

Особенность только в том, что строка regex при этом случайно не была задана. Может, так и должно быть...
   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)

Другие пустые match-строки в непустых ячейках, естественно, пропускает, как и должно быть.

А по заверениям Eike Rathke такого не должно быть в принципе (привожу коммент в ответ на мой):
Yes (and you can't change it so it's not even a default), but css::util::TextSearch does all sort of tweaking for backwards (also API behaviour) compatibility with the old OOo engine plus some, including skipping zero-length matches.

Но, по-видимому, при условии, что использование регулярных выражений задано, а значение самого выражения отлично от пустой строки. Иначе, как мы видим, поиск происходит по другому алгоритму.
Ubuntu 18.04 LTS • LibreOffice 7.5.1.2 Community