Советы и рекомендации по разделению строк в Джулии

Разбиение строки — одна из самых распространенных операций, которую вы можете выполнять в любом языке программирования. В этой статье мы рассмотрим различные способы разбиения строк в Julia, включая обширные примеры различных подходов.

Правка: мой соавтор и я рады сообщить, что предварительные заказы на нашу новую книгу «Ускоренный курс Джулии» уже доступны:



Основной синтаксис разделения 🖖

Мы начнем с объявления базовой строки:

julia> my_string = "Hello.World"
"Hello.World"

Затем мы вызовем функцию split() и передадим строку, а также разделитель (то, что вы хотите разделить).

julia> split_strings = split(my_string, ".")
2-element Vector{SubString{String}}:
"Hello"
"World"

В этом случае, поскольку в строке был только 1 период, мы получаем две отдельные строки. Обратите внимание, что в новые строки не включен разделитель, поэтому общая длина объединенных строк в этом случае уменьшилась на 1. Давайте рассмотрим более надежный пример разделения строки:

julia> my_sentence = "Hello world. This is an extended example with more sentences to show what happens. I am going to add only a few more. Okay, last one. Wait, what is this?"

Я просто хочу подчеркнуть, как это выглядит, когда нужно разделить больше элементов. Давайте посмотрим на это в действии:

julia> split_sentences = split(my_sentence, ".")
5-element Vector{SubString{String}}:
"Hello world"
" This is an extended example with more sentences to show what happens"
" I am going to add only a few more"
" Okay, last one"
" Wait, what is this?"

Обратите внимание, что снова знаки препинания исчезли, но в начале предложения есть пробел. Нам пришлось бы использовать вызов функции strip, чтобы удалить его.

На данный момент это самая основная форма использования функции split. Далее мы рассмотрим более продвинутые варианты использования!

Еще одна вещь, о которой следует помнить, это то, что если мы не укажем разделитель, функция split по умолчанию использует пробел в качестве разделителя, если вы забудете добавить его, вы получите совершенно другой результат:

julia> split_sentences = split(my_sentence)
30-element Vector{SubString{String}}:
"Hello"
"world."
"This"
"is"
⋮
"what"
"is"
"this?"

Расширенные примеры разделения 🧗

Если вы хотите выйти за рамки основ использования split и попробовать несколько продвинутых примеров, этот раздел для вас. Мы начнем с игры с необязательным аргументом limit, который позволяет нам установить максимальное количество элементов, которые мы хотим создать.

julia> split_sentences = split(my_sentence, limit=10)
10-element Vector{SubString{String}}:
"Hello"
"world."
...
"with"
"more"
"sentences to show what happens." ⋯ 40 bytes ⋯ ", last one. Wait, what is this?"

Поскольку мы установили ограничение на 10, мы фактически прекращаем разбиение по разделителю после 10-го элемента. Последний элемент в этом векторе содержит несколько предложений.

Далее мы рассмотрим необязательный аргумент keepempty. Это позволяет нам указать, хотим ли мы сохранить пустые элементы в результирующем векторе. Вероятно, проще всего увидеть это на практике. Мы переопределим нашу строку, чтобы включить больше текста:

julia> my_sentence = "Hello world. This is an extended example with more sentences to show what happens. I am going to add only a few more. Okay, last one. Wait, what is this? . . . . . . . . ........"
"Hello world. This is an extended example with more sentences to show what happens. I am going to add only a few more. Okay, last one. Wait, what is this? . . . . . . . . ........"

Теперь давайте посмотрим на это в действии с обоими вариантами:

julia> split_sentences = split(my_sentence, ".", keepempty=false)
13-element Vector{SubString{String}}:
"Hello world"
" This is an extended example with more sentences to show what happens"
" I am going to add only a few more"
" Okay, last one"
" Wait, what is this? "
...
" "
" "

Здесь мы видим, что, поскольку keep empty имеет значение false, у нас нет результирующих элементов, где значением является пустая строка (""). Если мы изменим значение на true, мы получим следующее:

julia> split_sentences = split(my_sentence, ".", keepempty=true)
21-element Vector{SubString{String}}:
"Hello world"
" This is an extended example with more sentences to show what happens"
" I am going to add only a few more"
" Okay, last one"
" Wait, what is this? "
" "
⋮
""
""

Опять же, поскольку в исходном примере много точек подряд ( ....... ), параметр keepempty, установленный на true, дал нам кучу пустых строк.

Это все, что мы можем сделать с базовой функцией split. Давайте рассмотрим некоторые другие способы обработки разбиения строк в Julia!

Base.rsplit( )— Начиная с конца 🧵

Подобно функции разделения, существует также функция rsplit, которая делает то же самое, что и функция разделения, но начинается с конца (хотя, что интересно, порядок результирующих данных не меняется на обратный). Рассмотрим простой пример на практике:

julia> my_string = "Hello.World.This.Is.A.Test"
"Hello.World.This.Is.A.Test"

Теперь давайте сравним, чем это отличается от обычной функции split:

julia> a = split(my_string, ".")
6-element Vector{SubString{String}}:
"Hello"
"World"
"This"
"Is"
"A"
"Test"

julia> b = rsplit(my_string, ".")
6-element Vector{SubString{String}}:
"Hello"
"World"
"This"
"Is"
"A"
"Test"

julia> a == b
true

Но подождите секунду, если rsplit это:

Аналогично split, но начиная с конца строки.

почему не инвертирует порядок? Что ж, это отличный вопрос и то, что я задал на Stack Overflow, чтобы попытаться получить контекст для этого.

Самый большой раз это будет иметь значение, если вы предоставите аргумент limit. В этом случае результаты будут другими:

julia> a = split(my_string, "."; limit=2)
2-element Vector{SubString{String}}:
"Hello"
"World.This.Is.A.Test"
julia> b = rsplit(my_string, "."; limit=2)
2-element Vector{SubString{String}}:
"Hello.World.This.Is.A"
"Test"
julia> a == b
false

Все остальные параметры аргумента остаются верными для rsplit, поэтому нет необходимости повторно хэшировать эти детали.

Использование eachsplit — введено в Юлии 1️⃣.8️⃣

В Julia 1.8+ есть новая функция eachsplit, которая позволяет вам разделять элементы так же, как мы это делали раньше, но в этом случае мы по умолчанию возвращаем итератор. Это может быть полезно, когда вы хотите работать с итератором, а не просто возвращать вектор по умолчанию. Давайте посмотрим на это в действии:

julia> a = "Ma.rch"
"Ma.rch"
julia> split(a, ".")
2-element Vector{SubString{String}}:
"Ma"
"rch"
julia> eachsplit(a, ".")
Base.SplitIterator{String, String}("Ma.rch", ".", 0, true)

Теперь, если мы хотим воспроизвести поведение функции split, нам нужно сделать следующее:

julia> collect(eachsplit(a, "."))
2-element Vector{SubString}:
"Ma"
"rch"

Функция collect позволяет нам объединять элементы через итератор. Лично я думаю, что документация здесь нуждается в некотором улучшении, поэтому у меня есть открытая проблема, направленная на решение этой проблемы.

Но что такое итератор для начала? Ну, согласно документам:

Последовательная итерация реализуется функцией iterate. Вместо того, чтобы изменять объекты по мере их повторения, итераторы Julia могут отслеживать состояние итерации вне объекта. Возвращаемое значение от iterate всегда либо кортеж значения и состояния, либо ничего, если не осталось элементов.

Если вы хотите узнать об этом больше, ознакомьтесь с разделом полная документация по итераторам.

Подводим итоги 🎁

В этом посте мы рассмотрели основы использования функции split вместе с rsplit и eachsplit. Эти функции обеспечивают базовую основу для разделения строк в Julia.

В String-averse (ха, понял) есть еще много всего, что можно исследовать. Я предлагаю проверить https://github.com/JuliaString/Strs.jl в качестве отправной точки для того, что возможно!