比正则表达式更快地匹配字符串之间的字符
Faster way match characters between strings than Regex?
用例是我想将查询字符串与单词数组进行比较,并返回匹配项。匹配是指单词包含查询字符串中的所有字符,顺序无关紧要,重复字符都可以。正则表达式似乎太强大了(只需要锤子的大锤(。我编写了一个解决方案,通过循环访问字符并使用 indexOf 来比较字符,但它似乎一直很慢。(http://jsperf.com/indexof-vs-regex-inside-a-loop/10(正则表达式是此类操作的最快选择吗?有没有办法让我的替代解决方案更快?
var query = "word",
words = ['word', 'wwoorrddss', 'words', 'argument', 'sass', 'sword', 'carp', 'drowns'],
reStoredMatches = [],
indexOfMatches = [];
function match(word, query) {
var len = word.length,
charMatches = [],
charMatch,
char;
while (len--) {
char = word[len];
charMatch = query.indexOf(char);
if (charMatch !== -1) {
charMatches.push(char);
}
}
return charMatches.length === query.length;
}
function linearIndexOf(words, query) {
var wordsLen = words.length,
wordMatch,
word;
while (wordsLen--) {
word = words[wordsLen];
wordMatch = match(word, query);
if (wordMatch) {
indexOfMatches.push(word);
}
}
}
function linearRegexStored(words, query) {
var wordsLen = words.length,
re = new RegExp('[' + query + ']', 'g'),
match,
word;
while (wordsLen--) {
word = words[wordsLen];
match = word.match(re);
if (match !== null) {
if (match.length >= query.length) {
reStoredMatches.push(word);
}
}
}
}
请注意,您的正则表达式是错误的,这肯定是它运行如此之快的原因。
现在,如果您的查询是"word"(如您的示例所示(,则正则表达式将是:
/[word]/g
这意味着查找其中一个字符:"w"、"o"、"r"或"d"。如果匹配,则 match(( 返回 true。做。绝对比最肯定更正确的 indexOf(( 快得多。(即,在简单 match(( 调用的情况下,"g"标志将被忽略,因为如果任何一件事匹配,该函数将返回 true。
另外,您提到了任意数量字符的想法/概念,我想如下所示:
'word', 'wwoorrddss'
indexOf(( 肯定不会正确捕获它,如果你真的意味着每个字符的"任何数字"。因为您应该匹配无限数量的案例。像这样的东西作为正则表达式:
/w+o+r+d+s+/g
你肯定很难用普通的JavaScript编写正确的代码,而不是使用正则表达式。但是,无论哪种方式,这都会有些慢。
从下面的评论中,单词的所有字母都是必需的,为此,您必须对 3 个字母的单词进行 3 个测试(3 个阶乘(:
/(a.*b.*c)|(a.*c.*b)|(b.*a.*c)|(b.*c.*a)|(c.*a.*b)|(c.*b.*a)/
显然,阶乘会非常迅速地增加你的可能性数量,并在超长的正则表达式中吹走你的记忆(尽管如果一个单词多次具有相同的字母,你可以简化,但你不必多次测试该字母(。
1! = 1
2! = 2
3! = 6
4! = 24
5! = 120
6! = 720
...
这可能就是为什么你在普通 JavaScript 中正确编写的测试要慢得多的原因。
此外,在您的情况下,您应该几乎像在拼字游戏词典中一样编写单词:所有字母都按字母顺序排列(拼字游戏保留重复项(。所以"字"就是"dorw"。正如您在示例中所示,单词"wwoorrddss"将是"dorsw"。你可以有一些后端工具来生成你的单词表(所以你仍然把它们写成"word"和"words",你的工具会把它们转换为"dorw"和"dorsw"。然后,您可以按字母顺序对正在测试的单词的字母进行排序,结果是您不需要正则表达式的愚蠢阶乘,您可以简单地这样做:
/d.*o.*r.*w/
这将匹配任何包含"单词"的单词,例如"密码"。
对字母进行排序的一种简单方法是将您的单词拆分为一个字母数组,然后对数组进行排序。您可能仍会收到重复项,这取决于排序功能。(我不认为默认的JavaScript排序会自动删除重复项。
还有一个细节,如果你的测试应该是不区分大小写的,那么你希望在运行测试之前将字符串转换为小写。所以像这样:
query = query.toLowerCase();
在您的顶级功能的早期。
您正在尝试加快算法"单词中的字符是查询字符的子集"。您可以缩短此检查并避免某些分配(这些分配更具可读性,但并非严格需要(。尝试以下版本的匹配
function match(word, query) {
var len = word.length;
while (len--) {
if (query.indexOf(word[len]) === -1) { // found a missing char
return false;
}
}
return true; // couldn't find any missing chars
}
这提供了 4-5 倍的改进
根据应用程序的不同,您可以尝试对单词进行预排序,并将单词中的每个单词预排序为另一种优化。
正则表达式匹配算法构造一个有限状态自动机,并根据从左到右读取的当前状态和字符做出决定。 这包括阅读每个字符一次并做出决定。
对于静态字符串(在几个文本上查看固定字符串(,你有更好的算法,比如 Knuth-Morris 允许你一次比一个字符更快,但你必须明白,这个算法不是用于匹配正则表达式,只是普通字符串。
如果您对Knuth-Morris感兴趣(还有其他几种算法(,请在维基百科中进行一轮。 http://en.wikipedia.org/wiki/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm
您可以做的一件好事是调查正则表达式匹配例程是否使用 DFA 或 NDFA 执行此操作,因为 NDFA 占用的内存更少并且更容易计算,但 DFA 做得更快,但会有一些编译惩罚和占用更多的内存。
Knuth-Morris算法还需要在工作之前将字符串编译成自动机,所以如果你只是使用它来查找某个字符串中的一个单词,它可能不适用于你的问题。
- 是否有任何JavaScript UI组件可以显示字符串之间的差异
- 使用Javascript获取两个字符串之间的字符串数组
- 如何在 javascript 中使用正则表达式在其他两个字符串之间找到一个字符串
- javascript:获取特定字符串之间的字符串
- Javascript Regex匹配两个字符串之间的子字符串,但子字符串可以包含DOT(.)
- 在jQuery或JavaScript中获取两个字符串之间的文本
- 在维护标识标签的字符串之间进行重复数据消除
- 在jQuery中,可以在字符串之间添加条件逻辑吗
- 什么's特征检测、特征推断和使用UA字符串之间的区别
- 替换两个字符串之间的多行文本
- 数组值和字符串之间的 JavaScript 比较
- 正则表达式获取两个字符串之间的所有字符串
- 如何使用正则表达式获取两个特定字符串之间的字符串
- 如何使用 javascript 删除字符串之间的标记
- 如何使用nodejs在两个字符串之间选择文本
- 正则表达式在字符串中搜索两个字符串之间的内容
- 查找/获取字符串之间的特定字符串
- Javascript 正则表达式替换 2 个字符串之间的字符串
- 匹配两个字符串之间的字符串
- 在 JavaScript 代码中的注释字符串之间匹配文本