如何处理正则表达式中的冗余案例?

How to handle redundant cases in regex?

我必须将一个文件数据解析成好的和坏的记录数据应该是这样的格式

Patient_id::Patient_name (year of birth)::disease

疾病是管道分隔的并且选自以下:

1.HIV
2.Cancer
3.Flu
4.Arthritis 
5.OCD

示例:23::Alex.jr (1969)::HIV|癌症|流感

我写的正则表达式是

\d*::[a-zA-Z]+[^\(]*\(\d{4}\)::(HIV|Cancer|flu|Arthritis|OCD) 
     (\|(HIV|Cancer|flu|Arthritis|OCD))*

但它也在考虑具有冗余条目的记录

24::罗宾 (1980)::HIV|癌症|癌症|HIV

如果疾病列表非常大,如何处理此类记录以及如何编写更好的表达式。

注意:我正在使用 hadoop maponly 作业进行解析,因此请根据 java 的上下文给出答案。

您需要负面前瞻。 尝试使用此正则表达式:^\d*::[^(]+?\s*\(\d{4}\)::(?!.*(HIV|Cancer|flu|Arthritis|OCD).*\|)((HIV|Cancer|flu|Arthritis|OCD)(\||$))+$.

解释:

  1. 初始字符串 ^\d*::[^(]+?\s*\(\d{4}\):: 只是经过优化以匹配 Alex.jr 示例(您的版本不尊重名称中的任何非字母符号)
  2. 否定先行块(?!.*(HIV|Cancer|flu|Arthritis|OCD).*\|)代表“寻找任何疾病名称,遇到两次,如果发现任何,则拒绝该字符串。其显着特点是(?! ... ) 签名。
  3. 最后,((HIV|Cancer|flu|Arthritis|OCD)(\||$))+$也是您的块(HIV|Cancer|flu|Arthritis|OCD)(\|(HIV|Cancer|flu|Arthritis|OCD))*的优化版本,旨在避免冗余列表。

你可能会做的是捕获一组中所有疾病的最后一部分(命名为捕获组 disease),然后使用 split 得到单独的部分,然后使列表唯一。

^\d*::[a-zA-Z]+[^\(]*\(\d{4}\)::(?<disease>(?:HIV|Cancer|flu|Arthritis|OCD)(?:\|(?:HIV|Cancer|flu|Arthritis|OCD))*)$

例如:

String regex = "^\d*::[a-zA-Z]+[^\(]*\(\d{4}\)::(?<disease>(?:HIV|Cancer|flu|Arthritis|OCD)(?:\|(?:HIV|Cancer|flu|Arthritis|OCD))*)$";
String string = "24::Robin (1980)::HIV|Cancer|Cancer|HIV";

Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(string);

if (matcher.find()) {
    String[] parts =  matcher.group("disease").split("\|");
    Set<String> uniqueDiseases = new HashSet<String>(Arrays.asList(parts));
    System.out.println(uniqueDiseases);
}

结果:

[HIV, Cancer]

Regex demo | Java demo

可能更容易维护的方法是你使用稍微改变的正则表达式, 如下所示:

^\d*::[a-zA-Z.]+\s\(\d{4}\)::((?:HIV|Cancer|flu|Arthritis|OCD|\|(?!\|))+)$

它包含:

  • ^$ 锚点(您希望匹配 整个 字符串, 不是它的一部分)。
  • 一个捕获组,包括一个重复的非捕获组(一个容器 替代品)。这些备选方案之一是 |,但带有负数 紧跟 | 的前瞻(这样你就不允许 2 或 更连续 |).

然后,如果此正则表达式与特定行匹配,您应该:

  • 将第 1 组拆分为 |
  • 检查结果字符串数组的唯一性(它不应该包含 重复条目)。

仅当此检查成功时,您才应该接受有问题的行。