代码之家  ›  专栏  ›  技术社区  ›  rinkjames

R: 不递归替换冲突替换的str_replace_all的有效方法?

  •  3
  • rinkjames  · 技术社区  · 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
    

    多炭替代解决方案
    通过一些正则表达式修补,我设法让多字符字符串替换也能正常工作。另请参见基础R答案 posted 通过 SamR .

    c("<multi>", "<strings>") %>% str_replace_all("(<.+?>)|.", ~ set_names(cipher$byte, cipher$char)[.x])
    [1] "14" "16"
    

    最初的问题:

    你好,

    问题是
    首先,让我试着说明这个问题。假设我想应用以下密码对字符串进行编码, "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 等等。这就是我所说的冲突替换的递归替换。

    相关信息
    我读过 this post 我也读过 this issue 。我还研究了 vectorize_all 论点 stri_replace_all* function .

    有效(但效率低下)的解决方案
    我成功地用替换值进行多个字符串替换的唯一方法是将每个字符串字符拆分为一个字符,然后进行替换,最后将它们全部粘贴在一起。像这样:

    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年的英特尔Macbook pro上)。我更喜欢在内部工作 tidyverse ,但在这个阶段,我也会考虑其他方法。

    1 回复  |  直到 1 年前
        1
  •  2
  •   SamR    1 年前

    注:此答案适用于题为 最初的问题 ,但不包括其上方显示的编辑。

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

    chartr 将每个字符翻译成 x 这在 old 到中指定的相应字符 new 规范中支持范围,但不支持字符类和重复字符。

    这个计划的一个小缺点是 图表() 只进行一对一的字符替换。您想替换 "a" 具有 "A0" 等等。但是,所有两个字符的替换都可以用十六进制表示为一个Unicode字符。

    我们可以编写一个函数,将每个单字符替换转换为实际的Unicode字符,以便在 图表() ,然后再回来。例如, “A0” 成为 intToUtf8("0xA0") ,即空间,然后将其送入 图表() 作为替代品 然后在最后转换回 “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"
    
    推荐文章