如何使用字符串规范解析字符串?

时间:2019-05-07 02:13:50

标签: java regex parsing regex-group regex-greedy

我希望编写一种方法来解析包含人物姓名及其年龄的字符串。例如:

Manuel 8
Mustafa 16
Zhihao 12
Itsuki 12
Louis 11
Farah 11

即字符串的规范为%N %A,其中%N代表名称,%A代表年龄。

但是,字符串的规范不是固定的(例如,在另一个文档中可以是%N age:%A%N (%A)),因此解析方法应能够将规范作为其参数之一

换句话说,解析方法应该像这样工作:

Data d1 = Parser.parse("Indira 15", "%N %A");
Data d2 = Parser.parse("12 Shu-chen", "%A %N");
Data d3 = Parser.parse("Hana (12)", "%N (%A)");
Data d4 = Parser.parse("Name: Sophia [12]", "Name: %N [%A]");

其中DataParser的定义如下:

public class Data {
    private String name;
    private int age;

    public Data(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // + getter and setter methods.
}

public class Parser {
    public static Data parse(String s, String specification) {
        // --- What to do here? ---
        return (new Data(name, age));
    }
}

如何编写Parser.parse?换句话说,如何使用字符串规范来解析字符串?

4 个答案:

答案 0 :(得分:1)

在这里,我们可以有一个表达式并将所需的输出分为两组,例如:

((?:\s+)?([a-z-]+)(?:\s+)?)|(\d+)

我们所需的名称在此([a-z-]+)组中,年龄信息在此(\d+)中,其余的可以简单地编写为脚本。

测试

import java.util.regex.Matcher;
import java.util.regex.Pattern;

final String regex = "((?:\\s+)?([a-z-]+)(?:\\s+)?)|(\\d+)";
final String string = "Indira 15\n"
     + "12 Shu-chen\n"
     + "Hana (12)\n"
     + "Sophia [12]\n"
     + "  Manuel 8\n"
     + "Mustafa  16\n"
     + "Zhihao    12\n"
     + "Itsuki 12\n"
     + "Louis 11\n"
     + "Farah 11";

final Pattern pattern = Pattern.compile(regex, Pattern.MULTILINE | Pattern.CASE_INSENSITIVE);
final Matcher matcher = pattern.matcher(string);

while (matcher.find()) {
    System.out.println("Full match: " + matcher.group(0));
    for (int i = 1; i <= matcher.groupCount(); i++) {
        System.out.println("Group " + i + ": " + matcher.group(i));
    }
}

DEMO

RegEx电路

jex.im可视化正则表达式:

enter image description here

DEMO 2

建议

根据zdim的建议:

  

(1)我认为(?:\ s +)? (至少一个空格,但整个过程是   可选)与\ s *(可选空格)

相同      

(2)在第二组空格中,我认为您想在   至少一个空格,所以\ s +。

我们可以大大简化和修改初始表达式,使其类似于:

(\s*([a-z-]+)\s+)|(\d+)

DEMO

答案 1 :(得分:0)

我将阅读该规范并将其用于解析不属于%N和%A的字符串的所有部分,因为它们只会使您的逻辑复杂化。然后,您必须弄清楚打算对该API施加哪些约束,就好像您不对其不确定性施加约束一样。例如

H'mil99是%N%A模式,但是99岁的Nam H'mil或9岁的H'mil9。

如果您选择名称中没有数字且年龄没有字母的逻辑限制,则通过在[0-9] *上执行matcher.group来识别年龄,并将其与%A和%N,其余为%N

答案 2 :(得分:0)

specification构建正则表达式,例如像下面这样。

请注意使用Pattern.quote()来确保specification中的特殊字符不会被解释为正则表达式。

public static Data parse(String s, String specification) {
    // Determine order of value markers
    int nameIdx = specification.indexOf("%N");
    if (nameIdx == -1)
        throw new IllegalArgumentException("Specification is missing %N: " + specification);
    int ageIdx = specification.indexOf("%A");
    if (ageIdx == -1)
        throw new IllegalArgumentException("Specification is missing %A: " + specification);

    // Build regex
    String regex;
    if (nameIdx < ageIdx) {
        regex = Pattern.quote(specification.substring(0, nameIdx)) + "(\\S+)" +
                Pattern.quote(specification.substring(nameIdx + 2, ageIdx)) + "(\\d+)" +
                Pattern.quote(specification.substring(ageIdx + 2));
    } else {
        regex = Pattern.quote(specification.substring(0, ageIdx)) + "(\\d+)" +
                Pattern.quote(specification.substring(ageIdx + 2, nameIdx)) + "(\\S+)" +
                Pattern.quote(specification.substring(nameIdx + 2));
    }

    // Parse string
    Matcher m = Pattern.compile(regex).matcher(s);
    if (! m.matches())
        throw new IllegalArgumentException("String does not fit specification '" + specification + "': " + s);
    String name, age;
    if (nameIdx < ageIdx) {
        name = m.group(1);
        age = m.group(2);
    } else {
        name = m.group(2);
        age = m.group(1);
    }
    return new Data(name, Integer.parseInt(age));
}

测试

System.out.println(parse("Indira 15", "%N %A"));
System.out.println(parse("12 Shu-chen", "%A %N"));
System.out.println(parse("Hana (12)", "%N (%A)"));
System.out.println(parse("Name: Sophia [12]", "Name: %N [%A]"));

输出(假设toString()类中实现了Data

Data[name=Indira, age=15]
Data[name=Shu-chen, age=12]
Data[name=Hana, age=12]
Data[name=Sophia, age=12]

答案 3 :(得分:0)

这适用于给定的数据,应适用于其他变体。但是其他特殊字符可能无法正确转义。

import java.util.*;
import java.util.regex.*;

public class Parser2 {

   public static void main(String[] args) {
      Data d1 = Parser.parse("Indira 15", "%N %A");
      Data d2 = Parser.parse("12 Shu-chen", "%A %N");
      Data d3 = Parser.parse("Hana (12)", "%N (%A)");
      Data d4 = Parser.parse("Name: Sophia [12]", "Name: %N [%A]");

      System.out.println(d1);
      System.out.println(d2);
      System.out.println(d3);
      System.out.println(d4);

   }

}

class Data {
   private String name;
   private int    age;

   public Data(String name, int age) {
      this.name = name;
      this.age = age;
   }

   public String toString() {
      return "name = " + name + ", " + "age = " + age;
   }
}

class Parser {
   private static Map<String, String> spec =
         Map.of("%A", "(\\d+)", "%N", "([A-Za-z-]+)");

   public static Data parse(String s, String specification) {
      specification = specification.replaceAll("\\(", "\\\\(");
      specification = specification.replaceAll("\\)", "\\\\)");
      specification = specification.replaceAll("\\]", "\\\\]");
      specification = specification.replaceAll("\\[", "\\\\[");

      for (String r : spec.keySet()) {
         specification = specification.replace(r, spec.get(r));
      }

      Matcher m = Pattern.compile(specification).matcher(s);
      String m1 = "", m2 = "";
      if (m.find()) {
         m1 = m.group(1);
         m2 = m.group(2);
      } else {
         return null;
      }
      String name;
      int age;
      if (m1.matches("\\d+")) {
         age = Integer.parseInt(m1);
         name = m2;
      }
      else {
         age = Integer.parseInt(m2);
         name = m1;
      }

      return (new Data(name, age));
   }
}
  • ()和[]被转义,以使您的示例能够正常工作 是正则表达式的特殊字符。

  • 一个捕获的字符串中的所有数字都需要测试 允许对名称和年龄进行适当的转换。

  • 这是一种蛮力的方法,无法很好地扩展。