【问题标题】:Regular Expressions to Extract Non-Standard String提取非标准字符串的正则表达式
【发布时间】:2018-06-04 20:43:28
【问题描述】:

我的问题与使用 If-Then-Else 条件和 str_extract 的正则表达式中的前瞻和后视构造有关。

我在下表中有一个名为 UNIT 的字符串,需要将其分解为 3 个组成部分。格式是非标准的,我使用 regex 和 str_extract 为每个组件创建新列。

我可以很容易地提取字符串的开头(3A、3C 等)和结尾(E、A),但中间部分有点困难。它可以是 1-3 位数字,也可以是 SK、SD 或 HH 的两个字符代码。我可以单独使用下面的代码,但后一行会覆盖前一行。

那么,我的问题是,如何将正则表达式中的 If-Then-Else 条件 (?(?=regex)then|else) 与 str_extract 结合使用以从 df1 获取 df2?

df1$C2 = str_extract(df1$UNIT,"(?<=[:upper:])\\d*(?<![:upper:])")

df1$C2 = str_extract(df1$UNIT, "S.$")


df1
ID  UNIT
1   3ASD
2   3C14E
3   3D5E
4   3E15E
5   3ESK
6   3B14A
7   3BHHQ2
8   3E101

df2
ID  UNIT    C1  C2  C3
1   3ASD    3A  SD  NA
2   3C14E   3C  14  E
3   3D5E    3D  5   E
4   3E15E   3E  15  E
5   3ESK    3E  SK  NA
6   3B14A   3B  14  A
7   3BHHQ2  3B  HH  Q2
8   3E101   3E  101 NA

【问题讨论】:

  • 准确地说:C2 值只能是 3 个值中的 1 个,SK、SD 或 HH,还是 1-3 位? C1值是数字+大写?而 C3 是 1 个可以丢失的大写字母?
  • @WiktorStribiżew C2 值可以是 1-3 位数字(例如 5、55、555)或 SK、SD、HH 三个字符对。字符对是 C2 中唯一的字符类型。正确,C3 是一种可能会丢失的大写字母。
  • 嗯,C3 可以是字母+数字,#7 是Q2。对?我想你可以用str_match,试试str_match(df$UNIT, "^([0-9][[:upper:]])([0-9]{1,3}|S[KD]|HH)([[:upper:]][0-9]*)?$")
  • @WiktorStribiżew 该代码有效,但未获得 df2 中显示的所需产品。我解决了问题并将发布。
  • 那里有什么不同?您没有提供可重现的示例,我自己构建了 DF,并且由于您没有提供确切的规格,因此正则表达式与值匹配。

标签: r regex if-statement regex-lookarounds stringr


【解决方案1】:

我认为您可以在单个正则表达式中“编码”条件,用捕获组包装单独的值,然后使用 str_match 实际访问这些捕获,以便以后使用它们来创建列:

library(stringr)
df <- data.frame(ID=c(1,2,3,4,5,6,7,8), UNIT=c("3ASD","3C14E","3D5E","3E15E","3ESK","3B14A","3BHHQ2","3E101"))
rx = "^([0-9][[:upper:]])([0-9]{1,3}|S[KD]|HH)([[:upper:]][0-9]*)?$"
match_table <- str_match(df$UNIT, rx)
df$C1 <- match_table[,2]
df$C2 <- match_table[,3]
df$C3 <- match_table[,4]
> df
  ID   UNIT C1  C2   C3
1  1   3ASD 3A  SD <NA>
2  2  3C14E 3C  14    E
3  3   3D5E 3D   5    E
4  4  3E15E 3E  15    E
5  5   3ESK 3E  SK <NA>
6  6  3B14A 3B  14    A
7  7 3BHHQ2 3B  HH   Q2
8  8  3E101 3E 101 <NA>

请参阅regex demo

详情

  • ^ - 字符串开头
  • ([0-9][[:upper:]]) - 组 C1:一个数字,然后是一个大写字母
  • ([0-9]{1,3}|S[KD]|HH) - 组 C2:1、2 或 3 位数字,或 SKSDHH
  • ([[:upper:]][0-9]*)? - 可选组 C3:大写字母后跟 0+ 个数字
  • $ - 字符串结束,

【讨论】:

  • 谢谢,这是有道理的,我想知道为什么使用 str_match 和创建 match_table 比 str_extract 更可取。是性能问题吗?我应该提一下,这个数据集是 >1M 行。
  • 我不知道性能如何,因为你有数据。然而,运行单个正则表达式比运行 3 次计算成本更低,并且更精确,因为您不必担心上下文(无需使用环视或替换)。基本上,这里str_match是既安全又可读的KISS解决方案。
  • 这种方法效果很好,但是我错过了一个要求。该单元也可以仅以 C1 格式展示自己。例如,单位 = 3A、3C。您编写的正则表达式不会拾取这些实例。我如何将这个添加的要求集成到代码中?
  • @samuelt 将? 量词设置为第二组非常容易(与我在第三组中展示的相同)-"^([0-9][[:upper:]])([0-9]{1,3}|S[KD]|HH)?([[:upper:]][0-9]*)?$"
  • 效果很好,感谢您的帮助。我用过数据表法。
【解决方案2】:

你可以读入表格:

 cbind(df1,read.table(text=sub("(..)(\\d+|SK|SD|HH)(.*)","\\1 \\2 \\3",df1$UNIT),fill=T,h=F,col.names = c("C1","C2","C3"),na.strings = ""))
  ID   UNIT C1  C2   C3
1  1   3ASD 3A  SD <NA>
2  2  3C14E 3C  14    E
3  3   3D5E 3D   5    E
4  4  3E15E 3E  15    E
5  5   3ESK 3E  SK <NA>
6  6  3B14A 3B  14    A
7  7 3BHHQ2 3B  HH   Q2
8  8  3E101 3E 101 <NA>

【讨论】:

  • 与读取然后执行操作相比,读取它有什么好处?
  • 我假设你的数据已经被读入了。所以我们可以从已经读入的数据中创建一个表......如果你觉得这很困难,你可以这样做:@987654322 @ 除了您可以设置的列名之外,它仍然会给出相同的结果
【解决方案3】:
Problem solved using the following code:

        df2$C1=  str_extract(df1$Unit, "^[:digit:][:upper:]")
    #if the start of the string is a digit and upper case letter then extract it into col C1

        df2$C2= str_extract(df1$Unit,"(?<=[:upper:])\\d*(?<![:upper:])|(?<=[:upper:])[[:upper:]][[:upper:]](?<=[:upper:])")
    #if a digit follows an uppercase letter or is behind another uppercase letter then extract all digits in between and extract it into C2
    #OR if two uppercase letters follow an uppercase letter or come before another uppercase letter then extract all letters in between and extract it into C2

        df2$C3=  str_extract(df1$Unit, "(?<=[:digit:])[A-E]$|Q.$")
    #if a the last a letter is A-E and is preceded by a digit then extract the letter into C3
    #OR if the last character is preceded by the letter Q then extract Q and the character

【讨论】:

  • 你不需要 if else.. 你也不需要分解它们。只需一种正则表达式模式就足够了。了解更多关于正则表达式,然后您将了解 Wiktor 给出的内容
  • 现在与我的示例进行比较:1) C1: 你的和我的一样,^[:digit:][:upper:] = ^([0-9][[:upper:]]) - 字符串开头 + 大写,2) 你可能 0+ 数字/ 2 个大写字母之间的 2 个大写字母 - 但您说大写字母仅限于 SK|SD|HH - 那么,该相信什么? 3) 你可以在字符串末尾有一个从AE 的字母,或者Q 后跟任何字符,例如与§。如果这是正确的规格,您应该在问题中提供它。我认为我的解决方案正是您所需要的 - 只需精确要求即可。
猜你喜欢
  • 2021-01-17
  • 2010-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-05
  • 2013-08-03
  • 1970-01-01
  • 2014-08-25
相关资源
最近更新 更多