R:str_replace_all 的有效方法,无需递归替换冲突的替换?

问题描述 投票:0回答:1

更新:

发现了优雅的解决方案
令人尴尬的是,我在发布原始问题后不久就发现了一个非常优雅的解决方案。我想知道为什么 this 没有在我最初的谷歌搜索中出现......

解决方案很简单:

c("abc", "123") %>% str_replace_all(".", ~ set_names(cipher$byte, cipher$char)[.x])
[1] "A0A1A2" "F7F8F9"

并发症
然而,我仍然可以使用一些帮助来解决原始问题的复杂化。如果我向密码添加多字符字符串替换,我会得到

NA
值。怎么解决这个问题?

library(tidyverse)

cipher <- tibble(
  byte = c(20:22, 128:153, 160:185, 246:255) %>% as.hexmode() %>% str_to_upper(),
  char = c("<multi>", "<char>", "<strings>", LETTERS, letters, 0:9)
)

c("<multi>","<strings>") %>% str_replace_all(".", ~ set_names(cipher$byte, cipher$char)[.x])
[1] NA NA

原问题:

你好,

问题
首先,让我试着说明一下这个问题。假设我想应用以下密码来编码字符串,

"abc"

library(tidyverse)

cipher <- tibble(
  byte = c(128:153, 160:185, 246:255) %>% as.hexmode() %>% str_to_upper(),
  char = c(LETTERS, letters, 0:9)
)

"abc" %>% str_replace_all(set_names(cipher$byte, cipher$char))
[1] "AFFCAFFDAFFE"

我想要的结果是

"A0A1A2"
,而不是
"AFFCAFFDAFFE"
。看起来第一个替换
0
中的
A0
被替换为它自己的替换,即
FF
,依此类推。这就是我所说的冲突替换的递归替换的意思。

相关信息
我已阅读这篇文章。我也读过这个问题。我还研究了

vectorize_all
function
stri_replace_all* 参数。

有效(但效率低下)的解决方案
我成功地使用可能会发生冲突的替换值进行多个字符串替换的唯一方法是逐个字符地拆分每个字符串字符,然后进行替换,最后将其全部粘贴在一起。像这样:

library(tidyverse)

c("abc", "123") %>%
  map_chr(\(string) {
    str_split_1(string, "") %>%
      map_chr(\(char) {
        str_replace_all(char, set_names(cipher$byte, paste0("^", cipher$char, "$")))
      }) %>% paste(collapse = "")
  })
[1] "A0A1A2" "F7F8F9"

不幸的是,对于大型向量,这种字符串编码方式需要很长时间(至少在我的 2020 Intel Macbook pro 上)。我更喜欢在

tidyverse
内工作,但现阶段我也会考虑其他方法。

r regex tidyverse hex substitution
1个回答
0
投票

您的解决方案很优雅。但是,当您实现密码时,我认为自然的方法是避免正则表达式并使用基本函数

chartr()
:

chartr
x
中指定的
old
中的每个字符转换为
new
中指定的相应字符。规范中支持范围,但不支持字符类和重复字符。

这个计划中的小问题是

chartr()
只进行一对一的角色替换。您想将
"a"
替换为
"A0"
等等。但是,所有两个字符替换都可以用十六进制表示为一个 Unicode 字符。

我们可以编写一个函数,将每个单个字符替换转换为实际的 Unicode 字符,以便我们可以在

chartr()
中使用它,然后再返回。例如,
"A0"
变成
intToUtf8("0xA0")
,即空间,然后将其输入到
chartr()
作为
"a"
的替代品,然后最后转换回
"A0"

do_cipher <- function(x, old = cipher$char, new = cipher$byte) {
    chartr(
        paste0(old, collapse = ""),
        intToUtf8(paste0("0x", new)),
        x
    ) |>
        vapply(
            \(x)
            x |>
                utf8ToInt() |>
                as.hexmode() |>
                paste0(collapse = "") |>
                toupper(),
            character(1)
        ) |>
        setNames(x)
}

这将返回所需的输出:

c("abc", "123") |> do_cipher()
#      abc      123
# "A0A1A2" "F7F8F9"
© www.soinside.com 2019 - 2024. All rights reserved.