让我们说我需要匹配一个单词word
,其中可能有一段时间在词之前或之后,但不是两者。然后是word
,.word
和word
。应匹配,但.word.
不应匹配。我如何匹配这个并捕获单词之前和之后发生的事情?
这是一个简化的例子,我需要扩展到更复杂的案例。例如,现在符号.
和'
可能出现在单词之前或之后,但它们只能出现一次。例如,.word
,'word
,word.'
和.word'
只是有效匹配中的一部分,但.'word.'
之类的内容不匹配,甚至是.'word'
。
以上示例是我的主要优先事项,但额外的好处是添加句点和撇号的顺序。因此'.word
和.'word
都应匹配。我认为应该对此有用的一种方法是\.?'?|'?\.?word
,但我希望某种方式,OR子句中的语句数量不依赖于符号数。
答案 0 :(得分:0)
确定。为了正确处理word
出现在字符串开头或结尾的情况,需要更多时间。
"(?:\.word(?:[^.]|$))|(?:(?:[^.]|^)word(?:[^.]|$))|(?:(?:[^.]|^)word\.)"
相同regexp
与 Lookaheads 和 Lookbehinds (在python中测试):
"(?:\.word(?:(?!\.)|$))|(?:(?:(?<!\.)|^)word(?:(?!\.)|$))|(?:(?:(?<!\.)|^)word\.)"
有效:
re.findall(pattern(above), '.word. .word .word. word.'") // return ['.word ', ' word.']
答案 1 :(得分:0)
这适用于javascript,包含您提供的好的和坏的值。
var func = function (str) {
var result = true, match, re = /^([^a-z]+)[a-z]+([^a-z]+)$/i;
if (re.test(str)) {
match = re.exec(str);
re = new RegExp("[" + match[1] + "]");
result = !re.test(match[2]);
}
return result;
};
这是一个简单的解释。 如果字符串在字母前后包含非字母,则非字母将被提取并相互测试。否定测试结果以确定单词是好还是坏。
str = .'word.
".'", "word", "."
/[.']/.test( "." )
func
函数需要一个单词(没有空格的字符)作为字符串。
如果你想检查一个句子然后用空格分割,那么检查每个单词。
这样的事情。
var sentence = "What does .'words'. means?";
var words = sentence.split(/\s+/g);
var areWordsOk;
for( var i = 0, len = words.length; i < len; i++ ){
areWordsOk = func( words[i] );
if( !areWordsOk ){
throw new Error( "bad word." ); // error is thrown
}
}
这是我的测试用例。 现场演示:http://jsfiddle.net/Tb68G/2 这是测试用例的来源。
var func = function (str) {
var result = true, match, re = /^([^a-z]+)[a-z]+([^a-z]+)$/i;
if (re.test(str)) {
match = re.exec(str);
re = new RegExp("[" + match[1] + "]");
result = !re.test(match[2]);
}
return result;
};
test("test good values", function () {
var arr = [
"word",
".word",
"word.",
".word",
"'word",
"word.'",
".word'"
];
var i = arr.length,
str;
while (i--) {
str = arr[i];
equal(func(str), true, str + " should be true.");
}
});
test("test bad values", function () {
var arr = [
".word.",
".'word.'",
".'word'.",
".'word'"
];
var i = arr.length,
str;
while (i--) {
str = arr[i];
equal(func(str), false, str + " should be false.");
}
});
答案 2 :(得分:0)
哪种味道?如果它是JavaScript,这应该有效:
(?:^|[^\w.'])(?=[.']*(word))(?!'*\.'*\1'*\.)(?!\.*'\.*\1\.*')([.']*)\1([.']*)
说明:
(?:^|[^\w.'])
- 确保word
不是较大单词的尾端,并阻止正则表达式绕过前导分隔符(.
或{{1} })如果他们在那里。
'
- 确保(?=[.']*(word\b))
不是更长字的开头,并且除了您选择的分隔符之前没有任何内容。这个词目前没有消耗,它只是在组#1中被捕获,所以它可以用来锚定接下来的两个前瞻。
word
- 仍然位于前导分隔符(如果有的话)之前,这可以确保,如果在该单词之前有(?!'*\.'*\1'*\.)
,那么之后就没有一个。{/ p>
.
- 这对(?!\.*'\.*\1\.*')
也是如此。
'
- 最后,继续使用该词以及任何前导或尾随分隔符,捕获#2和#3组中的那些。
如果您使用的是支持lookbehind的风味,它可能无济于事。大多数口味严格限制了后视镜中可以匹配的内容,使其无法完成此任务。上面的JavaScript正则表达式可能仍然是您的最佳选择。但是,这个正则表达式适用于.NET和JGSoft,这是我所知道的唯一支持完全不受限制的外观的风格:
([.']*)\1([.']*)
说明:
(?<=(?:\.(?<dot1>)|'(?<apos1>))*)\bword\b(?=(?:\.(?<dot2>)|'(?<apos2>))*)(?!\k<dot1>\k<dot2>|\k<apos1>\k<apos2>)
- 向后扫描分隔符字符。当每个匹配时,跟随它的空捕获组有效地将该角色标记为已被看到。
(?<=(?:\.(?<dot1>)|'(?<apos1>))*)
- 消费这个词。
\bword\b
- 提前划分更多的分隔符并将它们关闭,就像后视一样。
(?=(?:\.(?<dot2>)|'(?<apos2>))*)
- 断言点和撇号都不会出现在单词之前和之后。对空组的反向引用从不消耗任何字符,它只是断言该组已参与该匹配。
在这两种风格之后,Java的外观可能是最灵活的,但它也是出了名的错误。我应该能够通过将第一个(?!\k<dot1>\k<dot2>|\k<apos1>\k<apos2>)
更改为*
来将此正则表达式移植到Java,但它只会抛出“没有明显的最大长度”异常。同样,您可能最好使用上面与JavaScript兼容的正则表达式。
答案 3 :(得分:-1)
我认为正则表达式很酷......
但有时候,你需要使用其他方法,
看着这样一个看似简单的怪异表达......
我说CODE IT!
int findWord(string text, string word, char ch, int startIdx = 0)
{
while(startIdx < text.Length)
{
int indexOf = text.IndexOf(word, startIdx);
if (indexOf < 0) return -1;
char preChar = (char) 0;
char postChar = (char) 0;
if (indexOf > 0)
preChar = text[indexOf - 1];
if (indexOf < text.Length - word.Length)
postChar = text[indexOf + word.Length];
if ((preChar == ch) ^ (postChar == ch))
{
return indexOf;
}
startIdx = indexOf + word.Length + 1;
}
}
不是那么简单,它超过一行:) 但表现更好,如果你在一两个月后阅读它就可以理解。