Рядок тексту (англ. string) - це фрагмент тексту в лапках. Будь-яка версія R, яку зараз підтримують, кодує рядки тексту в UTF-8, тож можна підтримати більшість мов світу.
mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"
На жаль, ця термінологія може заплутати програмістів, знайомих з іншими мовами.
Хоча type тут має значення character, R не робить різниці між однією літерою (у кількох інших мовах її називають char, але не в R) і довгим рядком тексту.
І те, і те - це один елемент, і його можна записувати в одинарних ' ' або подвійних " " лапках без різниці.
Посібник зі стилю радить використовувати подвійні лапки, крім випадків, коли треба вжити подвійні лапки всередині рядка тексту без екранування.
str2 <- 'String with "quotes"' # convenience format
str2
#> [1] "String with \"quotes\"" # standard format
Отже, character vector - це вектор рядків тексту, де кожен рядок тексту є одним елементом.
s <- c("some", "strings")
s
#> [1] "some" "strings"
length(s)
#> [1] 2
length(s[1]) # the number of vector elements, not the string length
#> [1] 1
nchar(s[1]) # the number of characters in the string "some"
#> [1] 4
У наведеному вище прикладі length(s[1]) дорівнює length("some"): це кількість елементів вектора, а не довжина рядка тексту.
Маніпулювати окремим рядком тексту, звісно, можна, як у прикладі вище з nchar(), але для цього потрібні певні функції, про які йтиметься далі.
У base R можливо майже все, але багато програмістів вважають підтримку роботи з рядками тексту доволі обмеженою й заплутаною.
На щастя, є пакети tidyverse, значно інтуїтивніші, гнучкіші та самоузгоджені.
Досі є багато застарілого коду, що використовує функції для рядків тексту старого зразка. Це стосується більшості рішень спільноти на Exercism.
Крім того, для невеликих завдань буває корисно скористатися вбудованими функціями замість того, щоб імпортувати велику бібліотеку. Супроводжувачі треку R зазвичай роблять саме так, коли пишуть зразкові рішення, щоб перевірити вправи.
Тож знання старих підходів усе ще варто здобути.
ncharПовертає кількість символів у рядку тексту.
nchar("string")
#> [1] 6
nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7
Тема того, що таке «символ», (а вона непроста!) виходить за межі нашого розгляду. Звернімо увагу лише на кілька моментів у прикладах вище:
\0 (це додало б 1 до довжини).paste і paste0
Поєднує довільну кількість рядків тексту в один рядок тексту.
Для paste можна за бажанням указати роздільник: типове значення - пробіл.
paste0(v) рівнозначне paste(v, sep = "").
paste("bits", "of", "a", "string")
#> [1] "bits of a string"
paste0("bits", "of", "a", "string")
#> [1] "bitsofastring"
sprintfДля тоншого контролю над складанням рядків тексту R запозичує функцію sprintf з мови C (і з кількох пізніших мов: Вікіпедія наводить близько 30).
r <- 5.3
pi <- 3.14159
sprintf("A circle of radius %.1f has area %.2f", r, pi * r^2)
#> [1] "A circle of radius 5.3 has area 88.25"
trimwsПровідні та/або кінцеві пробіли можна прибрати за допомогою trimws():
s <- " messy string "
trimws(s)
#> [1] "messy string"
Є параметр which = "left" (або "right"), щоб не обрізати обидва кінці.
substrПрацює з одним рядком тексту й повертає підрядок між двома межами (включно).
substr("Exercism", 5, 7)
#> [1] "cis"
strsplitРозбиває рядок тексту на список R за заданим рядком-роздільником. Роздільник обовʼязковий (типового значення немає), але може бути порожнім рядком тексту.
strsplit("R, Python, Julia", ", ")
[[1]]
#> [1] "R" "Python" "Julia"
strsplit("Exercism", "")
[[1]]
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
Списки розглянемо в окремому концепті.
Якщо ми ще не дійшли до цієї частини силабусу, поки що досить знати, що функція unlist() (у цьому випадку) перетворить список на вектор.
unlist(strsplit("Exercism", ""))
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
Якщо бути поблажливими, можна сказати, що функції для рядків тексту старого зразка - не найкраще, що може запропонувати R (існують і менш поблажливі думки).
Щоб виправити ситуацію, бібліотека stringr розробляється з 2009 року і тепер є ключовою частиною збірки Tidyverse.
Імпортуймо її у свій простір імен за допомогою library(stringr) або library(tidyverse).
Ось деякі з ключових можливостей:
str_ і намагаються зробити призначення зрозумілим.Як і всі пакети Tidyverse, stringr має хорошу документацію, яку зазвичай пишуть самі автори пакета.
Використання конвеєрів |> для зʼєднання функцій детальніше розглянемо в інших частинах силабусу.
Поки що корисно знати ось що:
Приклад нижче ілюструє синтаксис. Окремі функції розглянемо пізніше.
library(stringr)
"Monday-25-February" |>
str_split("-") |>
unlist() |> # [1] "Monday" "25" "February"
str_sub(1, 3) |> # [1] "Mon" "25" "Feb"
str_to_upper() |> # [1] "MON" "25" "FEB"
str_flatten(collapse = ", ")
#> [1] "MON, 25, FEB"
str_length рахує кодові точки (подібно до nchars).
Різні інші функції працюють із пробілами, зокрема str_trim() і str_squish().
Це часто важливе очищення на початку будь-якого проєкту з аналізу даних.
# The first 3 kernels in JuPyteR notebooks:
str_length(c("Julia", "Python", "R")) #> [1] 1 6 5
#> [1] 5 6 1
str_pad(c("Julia", "Python", "R"), 8, "right") # add spaces if necessary
#> [1] "Julia " "Python " "R "
str_trim("Julia Python R ") # remove leading & trailing whitespace
#> [1] "Julia Python R"
str_squish("Julia Python R ") # trim, collapse multiple spaces
#> [1] "Julia Python R"
Функція str_sub(string, start, end) поверне підрядок від start до end (включно), а str_sub_all() може працювати з кількома рядками тексту.
Тут багато гнучкості.
start і end типово вказують на перший і останній символ відповідно.
Вони можуть бути векторами індексів.
На приємну відміну від індексації векторів, відʼємні значення відраховуються від кінця рядка тексту, як у Python.
s <- "abcdefgh"
str_sub(s, 2, 4)
#> [1] "bcd"
str_sub(s, 5)
#> [1] "efgh"
str_sub(s, -3, -2)
#> [1] "fg"
str_sub(s, c(1, 4), c(2, 5))
#> [1] "ab" "de"
str_sub(s, c(1, 4, 6), c(2, 5, -1))
#> [1] "ab" "de" "fgh"
s |> str_sub(2, 4) # these functions are designed for pipes
#> [1] "bcd"
str_flatten перетворює вектор символів на один рядок тексту.
Аргумент collapse вставляється між елементами, а типове його значення - "".
c("R", "Julia", "Python") |> str_flatten()
#> [1] "RJuliaPython"
c("R", "Julia", "Python") |> str_flatten(collapse = " - ")
#> [1] "R - Julia - Python"
str_c перетворює кілька векторів символів на один вектор символів, з повторенням елементів за потреби.
str_c(LETTERS[1:8], 1:8, sep = ":")
#> [1] "A:1" "B:2" "C:3" "D:4" "E:5" "F:6" "G:7" "H:8"
str_glue приносить у R просту інтерполяцію рядків.
Вирази у фігурних дужках { } обчислюються, а замість них підставляється їхній текстовий запис.
r <- 5.3
pi <- 3.14159
str_glue("A circle of radius {r} has area {pi * r^2}")
A circle of radius 5.3 has area 88.2472631
Дуже часте застосування R - приводити неохайні дані до єдиного формату для аналізу. Щоб допомогти з цим, є кілька способів розбити текст на підрядки. Більшість із них виходять за межі цього матеріалу.
str_split_1 - найпростіший: він перетворює один рядок тексту на вектор частин, розбиваючи його за вказаним шаблоном (типового шаблону немає).
s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R" "Julia" "Python"
Деякі мови (переважно європейські) розрізняють великі та малі літери.
У stringr є набір функцій, щоб перетворювати їх одне на одного з урахуванням локалі:
str_to_lower()str_to_upper()str_to_title() : перша літера кожного слова - у верхній регістрstr_to_sentence() : перша літера речення - у верхній регістрstr_to_upper("elixir") # defaults to locale = "en"
#> [1] "ELIXIR"
str_to_upper("ελληνικά") # infers Greek
#> [1] "ΕΛΛΗΝΙΚΆ"
str_to_title("the cat sat on the mat")
#> [1] "The Cat Sat On The Mat"
str_to_sentence("the cat sat on the mat")
#> [1] "The cat sat on the mat"
Для заміни підрядків за збігом із шаблоном str_replace() змінює перше входження шаблону, а str_replace_all() - усі входження.
Для заміни підрядків за індексами символів є форма присвоєння для str_sub().
s <- "abcde"
str_sub(s, 3, 4) <- "yz"
s
#> [1] "abyze"
У цій вправі ми обробляємо рядки логу.
Кожен рядок логу - це рядок тексту (англ. string), відформатований так: "[<LEVEL>]: <MESSAGE>".
Існує три різні рівні логу:
INFOWARNINGERRORНа нас чекають три завдання, і в кожному потрібно взяти рядок логу та щось із ним зробити.
Можна вільно використовувати як застарілі функції з базового R, так і бібліотеку stringr, і обидва підходи дають короткі робочі рішення.
Реалізуйте функцію message, яка повертає повідомлення з рядка логу:
message("[ERROR]: Invalid operation")
#> "Invalid operation"
Будь-які пробільні символи на початку й у кінці рядка потрібно прибрати:
message("[WARNING]: Disk almost full\r\n")
#> "Disk almost full"
Реалізуйте функцію log_level, яка повертає рівень логу з рядка логу; рівень має бути в нижньому регістрі:
log_level("[ERROR]: Invalid operation")
#> "error"
Реалізуйте функцію reformat, яка переформатовує рядок логу, ставлячи повідомлення першим, а рівень логу після нього в дужках:
reformat("[INFO]: Operation completed")
#> "Operation completed (info)"
Зареєструйтеся на Exercism, щоб вивчати й опановувати R, а також 21 концепція111 вправ та справжнє наставництво від людей, і все це безкоштовно.