Trilhas
/
R
R
/
Programa
/
Strings
St

Strings em R

2 exercícios

Sobre Strings

Contexto

Uma string, ou cadeia de caracteres, é um trecho de texto entre aspas.

mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"

Infelizmente, a terminologia pode confundir quem já programa em outras linguagens.

Apesar de o type ser character, o R não faz distinção entre uma única letra (chamada char em várias outras linguagens, mas não no R) e uma string longa. Ambos são um único item e podem ficar entre aspas simples ' ' ou duplas " ", indistintamente.

O guia de estilo recomenda usar aspas duplas, exceto quando você quer usar aspas duplas dentro da string sem escapá-las.

str2 <- 'String with "quotes"'  # convenience format
str2
#> [1] "String with \"quotes\""    # standard format

Um character vector é, portanto, um vetor de strings, em que cada string é um único elemento.

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

No exemplo acima, length(s[1]) é equivalente a length("some"): o número de elementos do vetor, não o comprimento da string.

Manipular uma string individual obviamente é possível, como com nchar() no exemplo acima, mas exige funções específicas, que serão discutidas abaixo.

Escape

Como vimos acima, alguns caracteres de pontuação precisam ser "escapados" com uma barra invertida \ antes deles.

A lista não vai surpreender a maioria dos programadores experientes, já que esse assunto é bem padronizado entre as linguagens.

Além das aspas (como mostrado acima), a lista inclui quebras de linha \n, tabulações \t, a barra invertida \\ e símbolos Unicode que começam com \U ou \u.

Para evitar usar muitos escapes, é possível definir strings cruas dentro de r"( )":

r"(raw "string")"
#> [1] "raw \"string\""

Para strings cruas que contêm parênteses ( ), existem muitas outras opções de sintaxe.

Localidades

Qualquer versão do R com suporte atual codifica strings em UTF-8, então a maioria das línguas do mundo pode ser atendida.

"आधुनिक मानक हिन्दी"
#> [1] "आधुनिक मानक हिन्दी"

Além disso, muitas funções do R levam em conta a locale e tentam seguir as convenções locais para ordenação de strings, conversão para maiúsculas/minúsculas, datas e horas, etc.

Este programa de estudos vai mostrar exemplos principalmente para o inglês dos EUA: tanto porque esse é um padrão comum, quanto porque os autores do programa de estudos moram nos EUA.

Localidades são um assunto complexo, então procure a documentação para a sua situação.

Funções de string

Quase tudo é possível no R base, mas muitos programadores acham o suporte à manipulação de strings bastante limitado e confuso.

Felizmente, existem pacotes do tidyverse que são bem mais intuitivos, flexíveis e coerentes entre si.

R base

Ainda existe muito código legado usando funções de string no estilo antigo. Isso inclui a maioria das soluções da comunidade no Exercism.

Além disso, para tarefas pequenas pode ser útil usar funções nativas em vez de importar uma biblioteca grande. Quem mantém a trilha de R costuma adotar essa abordagem ao escrever soluções exemplares para validar os exercícios no nosso repositório do GitHub.

Por isso, ainda vale a pena conhecer um pouco das abordagens antigas.

As funções abaixo são apresentadas na forma mais simples, mas a maioria também tem argumentos opcionais. Consulte a documentação para mais detalhes.

nchar

Retorna o número de caracteres em uma string.

nchar("string")
#> [1] 6

nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7

O assunto (complicado!) do que "caractere" significa está além do escopo deste texto. Repare só em dois pontos nos exemplos acima:

  • Apesar de serem implementadas em C internamente, as strings no R não terminam com \0 (o que acrescentaria 1 ao comprimento).
  • Caracteres Unicode de vários bytes são tratados como você esperaria (ou torceria). "Hrōðgār" tem 7 caracteres (também chamados de "runas" ou "pontos de código"), mas precisa de 10 bytes para ser armazenada.

Cuidado com o que você passa para nchar. Os resultados ainda podem ser imprevisíveis para valores que não são character, embora isso tenha melhorado nas versões recentes do R.

grep

Procura um padrão em um vetor de strings e retorna os índices que deram correspondência.

grep("Jav", c("Java", "Python", "Javascript"))
#> [1] 1 3
paste e paste0

Combina um número qualquer de strings em uma única string.

Opcionalmente, é possível especificar um separador para paste: o padrão é um espaço.

paste0(v) é equivalente a paste(v, sep = "").

paste("bits", "of", "a", "string")
#> [1] "bits of a string"
paste0("bits", "of", "a", "string")
#> [1] "bitsofastring"
sprintf

Para ter mais controle sobre a montagem de strings, o R copia a função sprintf do C (e de várias linguagens posteriores: a Wikipédia lista cerca de 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

Espaços em branco no início e/ou no fim podem ser removidos com trimws():

s <- "    messy string   "
trimws(s)
#> [1] "messy string"

Existe um parâmetro which = "left" (ou "right") para evitar remover as duas pontas.

substr

Atua sobre uma única string e retorna a substring entre dois limites (inclusive).

substr("Exercism", 5, 7)
#> [1] "cis"
strsplit

Divide uma string em uma lista do R, com base em uma string separadora. O separador é obrigatório (não tem padrão), mas pode ser uma string vazia.

strsplit("R, Python, Julia", ", ")
[[1]]
#> [1] "R"      "Python" "Julia" 

strsplit("Exercism", "")
[[1]]
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"

Listas serão abordadas em um conceito separado. Se você ainda não chegou a essa parte do programa de estudos, saiba por enquanto que a função unlist() vai (neste caso) converter uma lista em um vetor.

unlist(strsplit("Exercism", ""))
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
regexpr e regexpr

Abreviatura de "expressão regular", essas funções procuram ocorrências de um padrão em uma string e retornam uma lista de dados (potencialmente) úteis. Elas são mencionadas apenas por completude: existem opções melhores!

regexpr retorna apenas a primeira ocorrência; gregexpr retorna todas as ocorrências ("g" de busca "global").

Como os nomes sugerem, o padrão pode ser uma regex, que será abordada em um conceito posterior.

Advanced

Por que essas funções de string antigas parecem comandos do shell do Unix?

Embora o R tenha sido criado em 1993, ele foi projetado para ser em grande parte compatível com a linguagem S.

A linguagem S foi criada nos Bell Labs e apareceu pela primeira vez em 1976. Os Bell Labs também foram responsáveis pelo Unix (a partir de 1969) e pela linguagem C (1972).

Que equipe, vinda de uma era muito mais antiga! Só podemos supor que eles conversavam entre si e trocavam ideias.

Tidyverse

Sendo generosos, poderíamos dizer que as funções de string no estilo antigo não são o melhor que o R tem a oferecer (visões menos generosas também existem).

Para melhorar a situação, a biblioteca stringr está em desenvolvimento desde 2009 e hoje é parte essencial da coleção Tidyverse.

Importe a biblioteca para o seu namespace com library(stringr) ou library(tidyverse).

Alguns recursos importantes:

  • Todos os nomes de função têm o prefixo str_ e tentam deixar o propósito claro.
  • A ordem dos argumentos tenta ser coerente entre si, com os dados de entrada como primeiro argumento.
  • As entradas podem ir de uma única string a estruturas de dados complexas. As funções tentam se adaptar e fazer a coisa mais razoável.

Como todos os pacotes do Tidyverse, o stringr tem uma boa documentação, geralmente escrita por quem criou o pacote.

Há dezenas de funções disponíveis no stringr, e muitas vezes elas têm uma variedade de argumentos opcionais. Isso pode ser um pouco assustador no começo, então a documentação é sua amiga.

Pense no que você quer colocar (uma única string? um vetor? algo mais complexo?) e no que quer obter.

Provavelmente existe um jeito fácil de fazer o que você quer, se você encontrar a função certa e os argumentos certos.

Desvio: noções rápidas sobre pipes

O uso de pipes |> para conectar funções será discutido com mais detalhes em outras partes do programa de estudos.

Por enquanto, é útil saber que:

  • Um resultado à esquerda do pipe se torna o primeiro argumento da função à direita (em que "direita" ignora quebras de linha opcionais).
  • Os argumentos restantes podem ser incluídos na chamada da função.
  • No R, precisamos encadear com um pipe para uma chamada de função, não apenas para um nome, então os parênteses são sempre obrigatórios (isso é diferente de algumas outras linguagens).

O exemplo abaixo ilustra a sintaxe. As funções individuais serão discutidas mais adiante.

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"
Gerenciar comprimentos

A str_length conta pontos de código (semelhante a nchars).

Várias outras funções manipulam espaços em branco, incluindo str_trim() e str_squish(). Isso costuma ser uma tarefa de limpeza essencial no início de qualquer projeto de ciência de dados.

# 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"
Extrair partes de strings

A função str_sub(string, start, end) retorna a substring de start até end (inclusive), enquanto a str_sub_all() pode operar sobre várias strings.

Há bastante flexibilidade. start e end assumem, por padrão, o primeiro e o último caractere, respectivamente. Eles podem ser vetores de índices. Em contraste bem-vindo com a indexação de vetores, valores negativos contam a partir do fim da string, no estilo do 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_sub_all takes vector input, gives list output
str_sub_all(c(s, "ijklmnop"), c(1, 4, 6), c(2, 5, -1))
[[1]]
#> [1] "ab"  "de"  "fgh"

[[2]]
#> [1] "ij"  "lm"  "nop"
Juntar strings

A str_flatten converte um vetor de caracteres em uma única string. O argumento collapse é inserido entre cada elemento e tem "" como padrão.

c("R", "Julia", "Python") |> str_flatten()
#> [1] "RJuliaPython"
c("R", "Julia", "Python") |> str_flatten(collapse = " - ")
#> [1] "R - Julia - Python"

A str_c converte vários vetores de caracteres em um único vetor de caracteres, com reciclagem quando necessário.

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"

A str_glue traz a interpolação de strings simples para o R. As expressões dentro de chaves { } são interpretadas e a representação em texto é substituída.

Se várias strings de expressão forem fornecidas, elas também serão unidas como em str_flatten. As strings também podem, opcionalmente, ter os espaços em branco do início e do fim removidos.

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

Embora a str_glue seja flexível (e bem complicada), há uma variedade ainda maior de possibilidades na biblioteca glue, que está por baixo.

Dividir strings

Um uso muito comum do R é arrumar dados bagunçados em um formato consistente para análise. Para ajudar nisso, existem várias formas de dividir texto em substrings.

A str_split_1 é a mais simples: converte uma única string em um vetor de pedaços, divididos em algum padrão especificado (não existe padrão).

s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R"      "Julia"  "Python"

A str_split_i recebe um vetor de caracteres e um índice n e retorna um vetor com a n-ésima substring de cada string de entrada.

s2 <- c(s, "C Kotlin, F#")  #> [1] "R Julia Python" "C Kotlin, F#"
str_split_i(s2, " ", 2)  # 2nd element of each
#> [1] "Julia"   "Kotlin,"

A str_split recebe um vetor de caracteres e retorna uma lista de vetores. Há um conceito de listas no nosso programa de estudos que explica essa saída.

str_split(s2, " ")
[[1]]
#> [1] "R"      "Julia"  "Python"

[[2]]
#> [1] "C"       "Kotlin," "F#"     

Há também a str_split_fixed, que retorna uma matriz. Matrizes serão abordadas em um conceito posterior.

Comparar strings

Para comparar a string inteira, a str_equal() pode usar várias definições de "igualdade". Da forma mais simples, o padrão diferencia maiúsculas de minúsculas, mas isso pode ser mudado. Localidades e o Unicode criam muitas outras possibilidades.

str_equal("Odin", "odin")
#> [1] FALSE
str_equal("Odin", "odin", ignore_case = TRUE)
#> [1] TRUE

Existem muitas outras funções, que serão discutidas com mais detalhes no conceito de expressões regulares.

Transformar strings

Algumas linguagens (principalmente europeias) diferenciam letras MAIÚSCULAS e minúsculas. Existe um conjunto de funções do stringr para converter entre essas formas, de maneiras adequadas à localidade:

  • str_to_lower()
  • str_to_upper()
  • str_to_title(): primeira letra de cada palavra em maiúscula
  • str_to_sentence(): primeira letra da frase em maiúscula
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"

Para substituir substrings com base em correspondência de padrões, a str_replace() altera a primeira ocorrência do padrão e a str_replace_all() altera todas as ocorrências.

Para substituir substrings com base na indexação de caracteres, existe uma forma de atribuição da str_sub().

 s <- "abcde"
str_sub(s, 3, 4) <- "yz"
s
#> [1] "abyze"
Diversos

A str_dup cria duplicatas e a str_unique remove duplicatas.

Por padrão, a str_dup não tem separador, mas é possível especificar um.

str_dup("xyz", 4, sep = ", ")
#> [1] "xyz, xyz, xyz, xyz"

A str_unique é uma extensão da unique(). As duas removem entradas duplicadas de um vetor, mas a função do stringr também consegue lidar com diferença entre maiúsculas e minúsculas e com vários tipos de correspondência específicos de cada localidade.

inp <- c("some", "strings", "Some", "Strings")
str_unique(inp)
#> [1] "some"    "strings" "Some"    "Strings"
str_unique(inp, ignore_case = TRUE)
#> [1] "some"    "strings"
Advanced

A biblioteca stringr é baseada na biblioteca stringi, de funções de nível mais baixo.

Se você tiver necessidades específicas que ainda não foram abordadas acima, então:

  1. Consulte a documentação do stringr, que cobre mais opções do que conseguimos mencionar até neste longo documento de conceito.
  2. Consulte a documentação do stringi, que descreve muito mais funcionalidades.
Editar via GitHub O link abre em uma nova janela ou aba

Aprenda Strings

A prática está bloqueada

Desbloqueie mais 1 exercício para praticar Strings