匹配可能的令牌数组并返回匹配的令牌及其位置

时间:2015-02-23 15:15:06

标签: java arrays regex string

我正在用Java编写声音更改程序,只有当它们与模式字符串匹配时,它才能用替换字符串替换标记模式。

模式字符串可以使用%TokenName包含文字字符串和/或变量。这样的变量引用包含Token ListString的{​​{1}}类,其中包含可能的标记值。用于指定模式位置的可选锚点(正则表达式中的^$)可以在模式之前或之后。处理替换时删除所有空格。

以下示例应仅在首次出现ShortVowel令牌时匹配,后跟VoicelessStop,并且该字符串应结束:

%ShortVowel %VoicelessStop $

跟随令牌:

ShortVowel: ɑ ɛ ɪ jɪ ɔ ə
VoicelessStop: k p t

我希望replacer返回一个ReplacerMatch类的数组,其中包含List Stringdɛt个匹配的每个变量的标记,以及总匹配的开始和结束位置在要处理的字符串中。对于字符串中的每个匹配,数组中都存在这样的类。

这意味着字符串[ matches: [ɛ, t] startPosition: 1 endPosition: 3 ] 应该返回

drɛkɔp

并且字符串[ matches: [ɔ, p] startPosition: 4 endPosition: 6 ] 应该返回

因为只匹配字符串末尾的匹配项。字符串ReplacerMatch应该返回一个空数组。


public class ReplacerMatch { private List<String> matches; private int startPosition; private int endPosition; [...] } 类的定义如下:

Replacer

这样的替换规则在public class Replacer { enum Anchor { NONE, STRING_START, STRING_END; public static Anchor fromString(String string) { if (string.startsWith("^")) return STRING_START; else if (string.endsWith("$")) return STRING_END; else return NONE; } } private String pattern; private String replacement; private List<Token> tokens; [...] } 类中定义:

Token

String类包含该令牌的名称和包含可能令牌值的public class Token { private final String name; private final List<String> tokens; [...] } 列表。这些值可以是可变长度。

Replacer

到目前为止,我已在Token类中编写代码,将模式字符串拆分为Anchor列表并解压缩public ReplacerMatch[] matches(String string) { String pat = this.pattern; // Get anchor Anchor anchor = Anchor.fromString(pat); if (anchor == Anchor.STRING_START) pat = pat.substring(1); else if (anchor == Anchor.STRING_END) pat = pat.substring(0,pat.length() - 1); // Parse variables List<Token> vars = new ArrayList<>(); Pattern varPattern = Pattern.compile("%(\\w+)"); Matcher varMatcher = varPattern.matcher(pat); while (varMatcher.find()) { for (Token t : this.tokens) { if (t.getName().equals(varMatcher.group(1))) { vars.add(t); pat = pat.replace(varMatcher.group(),"%"); varMatcher.reset(pat); break; } } // Error handling on non-existing token } return new ReplacerMatch[0]; }

{{1}}

现在我坚持变量的匹配,这似乎是正则表达式相当困难或不可能。有人知道如何解决这个问题吗?

2 个答案:

答案 0 :(得分:0)

使用您的Token类,您可以通过

将其标记字段转换为Java Pattern对象
StringBuilder sb = new StringBuilder("[").append(tokens.get(0));
for (int i = 1; i < tokens.size(); i++){
    sb.append('|').append(tokens.get(i));
}
sb.append(']');
return sb.toString();

如果用用户提供的模式替换Token名称的每个实例,如Token模式,如

String javaPattern = userPattern.replaceAll("\\s+", "");
for (Token t : tokens){
    javaPattern = javaPattern.replaceAll('%'+t.getName(), t.toPatternString());
}
return Pattern.compile(javaPattern);

然后你得到一个匹配用户期望的模式,你只需要提取匹配的部分。

Matcher matcher = pattern.matcher(userInput);
if (matcher.matches()){
    // this gives you the limits
    matcher.start();
    matcher.end();

    // this is the matched bit
    String matchedString = matcher.group();
    // now you've got to follow your %some %some $ pattern to separate the parts of the matchedString. You have to parse your pattern into parts and for each of those part find the part in matchedString that matches
}

答案 1 :(得分:0)

为了完整性,以下是matches()类的已完成replace()Replacer方法:

public List<ReplacerMatch> matches(String string)
{
  String regex = this.pattern;
  List<ReplacerMatch> matches = new ArrayList<>();

  for (Token t : this.tokens)
    regex = regex.replaceAll('%' + t.getName(),t.toPattern());

  Pattern p = Pattern.compile(regex);
  Matcher m = p.matcher(string);
  while (m.find())
  {
    string = string.substring(0,m.start()) + Util.filledString('%',m.group().length()) + string.substring(m.end());

    List<String> local = new ArrayList<>(); 
    for (int i = 0; i < m.groupCount(); i ++)
      local.add(m.group(i + 1));

    matches.add(new ReplacerMatch(local,m.start(),m.end()));
  }

  return matches;
}

public String replace(String string)
{
  List<ReplacerMatch> matches = this.matches(string);
  if (matches.isEmpty())
    return string;

  int increase = 0;
  for (ReplacerMatch m : matches)
  {
    String replaced = this.replacement;
    for (int i = 0; i < m.getMatches().size(); i ++)
    {
      String match = m.getMatches().get(i);
      String pattern = "%" + (i + 1);
      replaced = replaced.replace(pattern,match);
    }

    string = string.substring(0,m.getStartPosition() + increase) + replaced + string.substring(m.getEndPosition() + increase);
    increase += (replaced.length() - m.getMatchesAsString().length());
  }
  return string;
}