我正在用Java编写声音更改程序,只有当它们与模式字符串匹配时,它才能用替换字符串替换标记模式。
模式字符串可以使用%TokenName
包含文字字符串和/或变量。这样的变量引用包含Token
List
个String
的{{1}}类,其中包含可能的标记值。用于指定模式位置的可选锚点(正则表达式中的^
和$
)可以在模式之前或之后。处理替换时删除所有空格。
以下示例应仅在首次出现ShortVowel
令牌时匹配,后跟VoicelessStop
,并且该字符串应结束:
%ShortVowel %VoicelessStop $
跟随令牌:
ShortVowel: ɑ ɛ ɪ jɪ ɔ ə
VoicelessStop: k p t
我希望replacer返回一个ReplacerMatch
类的数组,其中包含List
String
个dɛt
个匹配的每个变量的标记,以及总匹配的开始和结束位置在要处理的字符串中。对于字符串中的每个匹配,数组中都存在这样的类。
这意味着字符串[
matches: [ɛ, t]
startPosition: 1
endPosition: 3
]
应该返回
drɛkɔp
并且字符串[
matches: [ɔ, p]
startPosition: 4
endPosition: 6
]
应该返回
dɛ
因为只匹配字符串末尾的匹配项。字符串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}}
现在我坚持变量的匹配,这似乎是正则表达式相当困难或不可能。有人知道如何解决这个问题吗?
答案 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;
}