- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我目前正在研究 UNIX 风格的 glob 模式匹配的实现。一般来说, fnmatch
库是此功能的一个很好的引用实现。
看着一些the implementations ,以及阅读有关此的各种博客/教程,似乎该算法通常是递归实现的。
通常,任何需要“回溯”或需要返回到较早状态的算法都非常适合递归解决方案。这包括诸如树遍历或解析嵌套结构之类的事情。
但是我无法理解为什么特别是 glob 模式匹配经常以递归方式实现。我的想法是有时需要回溯,例如,如果我们有一个字符串 aabaabxbaab
, 和一个图案 a*baab
, *
后面的字符将匹配第一个“baab”子串,如 aa(baab)xbaab
,然后无法匹配字符串的其余部分。所以算法需要回溯以便字符匹配计数器重新开始,我们可以匹配第二次出现的 baab
,如:aabaabx(baab)
.
好的,但是当我们可能需要多个嵌套级别的回溯时,通常会使用递归,我不认为在这种情况下这是必要的。当模式上的迭代器和输入字符串上的迭代器无法匹配时,似乎我们一次只需要回溯一个级别。此时,模式上的迭代器需要移回最后一个“保存点”,它可能是字符串的开头,或者是最后一个 *
成功匹配的东西。这不需要堆栈 - 只需一个保存点。
我能想到的唯一复杂情况是出现“重叠”匹配。例如,如果我们有输入字符串 aabaabaab
, 和图案 a*baab
,我们将无法匹配,因为最后 baab
中的“b”可以是第一场比赛或第二场比赛的一部分。但是,如果最后一个模式迭代器保存点与模式末尾之间的距离大于输入迭代器位置与输入字符串末尾之间的距离,那么这似乎可以通过简单地回溯输入迭代器来解决。
所以,就我所见,迭代实现这个 glob 匹配算法应该不是什么大问题(假设一个非常简单的 glob 匹配器,它只使用 *
字符来表示“匹配零个或多个字符”。此外,匹配策略将是贪婪的。)
所以,我认为我对此肯定是错的,因为其他人都是递归地这样做 - 所以我一定错过了一些东西。只是我看不到我在这里遗漏了什么。所以我在 C++ 中实现了一个简单的 glob 匹配器(只支持 *
运算符),看看我是否能找出我遗漏了什么。这是一个非常直接、简单的迭代解决方案,它只使用一个内部循环来进行通配符匹配。它还记录了*
的索引。成对 vector 中的字符匹配:
bool match_pattern(const std::string& pattern, const std::string& input,
std::vector<std::pair<std::size_t, std::size_t>>& matches)
{
const char wildcard = '*';
auto pat = std::begin(pattern);
auto pat_end = std::end(pattern);
auto it = std::begin(input);
auto end = std::end(input);
while (it != end && pat != pat_end)
{
const char c = *pat;
if (*it == c)
{
++it;
++pat;
}
else if (c == wildcard)
{
matches.push_back(std::make_pair(std::distance(std::begin(input), it), 0));
++pat;
if (pat == pat_end)
{
matches.back().second = input.size();
return true;
}
auto save = pat;
std::size_t matched_chars = 0;
while (it != end && pat != pat_end)
{
if (*it == *pat)
{
++it;
++pat;
++matched_chars;
if (pat == pat_end && it != end)
{
pat = save;
matched_chars = 0;
// Check for an overlap and back up the input iterator if necessary
//
std::size_t d1 = std::distance(it, end);
std::size_t d2 = std::distance(pat, pat_end);
if (d2 > d1) it -= (d2 - d1);
}
}
else if (*pat == wildcard)
{
break;
}
else
{
if (pat == save) ++it;
pat = save;
matched_chars = 0;
}
}
matches.back().second = std::distance(std::begin(input), it) - matched_chars;
}
else break;
}
return it == end && pat == pat_end;
}
void test(const std::string& input, const std::string& pattern)
{
std::vector<std::pair<std::size_t, std::size_t>> matches;
bool result = match_pattern(pattern, input, matches);
auto match_iter = matches.begin();
std::cout << "INPUT: " << input << std::endl;
std::cout << "PATTERN: " << pattern << std::endl;
std::cout << "INDICES: ";
for (auto& p : matches)
{
std::cout << "(" << p.first << "," << p.second << ") ";
}
std::cout << std::endl;
if (result)
{
std::cout << "MATCH: ";
for (std::size_t idx = 0; idx < input.size(); ++idx)
{
if (match_iter != matches.end())
{
if (idx == match_iter->first) std::cout << '(';
else if (idx == match_iter->second)
{
std::cout << ')';
++match_iter;
}
}
std::cout << input[idx];
}
if (!matches.empty() && matches.back().second == input.size()) std::cout << ")";
std::cout << std::endl;
}
else
{
std::cout << "NO MATCH!" << std::endl;
}
std::cout << std::endl;
}
test("aabaabaab", "a*b*ab");
test("aabaabaab", "a*");
test("aabaabaab", "aa*");
test("aabaabaab", "aaba*");
test("/foo/bar/baz/xlig/fig/blig", "/foo/*/blig");
test("/foo/bar/baz/blig/fig/blig", "/foo/*/blig");
test("abcdd", "*d");
test("abcdd", "*d*");
test("aabaabqqbaab", "a*baab");
test("aabaabaab", "a*baab");
INPUT: aabaabaab
PATTERN: a*b*ab
INDICES: (1,2) (3,7)
MATCH: a(a)b(aaba)ab
INPUT: aabaabaab
PATTERN: a*
INDICES: (1,9)
MATCH: a(abaabaab)
INPUT: aabaabaab
PATTERN: aa*
INDICES: (2,9)
MATCH: aa(baabaab)
INPUT: aabaabaab
PATTERN: aaba*
INDICES: (4,9)
MATCH: aaba(abaab)
INPUT: /foo/bar/baz/xlig/fig/blig
PATTERN: /foo/*/blig
INDICES: (5,21)
MATCH: /foo/(bar/baz/xlig/fig)/blig
INPUT: /foo/bar/baz/blig/fig/blig
PATTERN: /foo/*/blig
INDICES: (5,21)
MATCH: /foo/(bar/baz/blig/fig)/blig
INPUT: abcdd
PATTERN: *d
INDICES: (0,4)
MATCH: (abcd)d
INPUT: abcdd
PATTERN: *d*
INDICES: (0,3) (4,5)
MATCH: (abc)d(d)
INPUT: aabaabqqbaab
PATTERN: a*baab
INDICES: (1,8)
MATCH: a(abaabqq)baab
INPUT: aabaabaab
PATTERN: a*baab
INDICES: (1,5)
MATCH: a(abaa)baab
"MATCH: "
之后的括号显示每个
*
匹配/消耗的子字符串特点。所以,这似乎工作正常,我似乎无法理解为什么有必要在这里回溯多个级别 - 至少如果我们限制我们的模式只允许
*
字符,我们假设贪婪匹配。
最佳答案
我没有检查你的实现的所有细节,但你可以在没有递归回溯的情况下进行匹配。
通过构建一个简单的有限状态机,您实际上可以在完全不回溯的情况下进行全局匹配。您可以将 glob 转换为正则表达式并以正常方式构建 DFA,或者您可以使用与 Aho-Corasick 机器非常相似的东西;如果你稍微调整一下你的算法,你会得到同样的结果。 (关键是您实际上不需要备份输入扫描;您只需要找出正确的扫描状态,这可以预先计算。)
经典的 fnmatch 实现没有针对速度进行优化;它们基于模式和目标字符串很短的假设。这种假设通常是合理的,但如果您允许不受信任的模式,您就会面临 DoS 攻击。并且基于该假设,类似于您提出的算法,不需要预先计算,在绝大多数用例中可能比任何需要预先计算状态转换表同时避免病理模式指数爆炸的算法更快。
关于c++ - 全局模式匹配的递归解决方案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30823596/
对此感到疯狂,真的缺少一些东西。 我有webpack 4.6.0,webpack-cli ^ 2.1.2,所以是最新的。 在文档(https://webpack.js.org/concepts/mod
object Host "os.google.com" { import "windows" address = "linux.google.com" groups = ["linux"] } obj
每当我安装我的应用程序时,我都可以将数据库从 Assets 文件夹复制到 /data/data/packagename/databases/ .到此为止,应用程序工作得很好。 但 10 或 15 秒后
我在 cc 模式缓冲区中使用 hideshow.el 来折叠我不查看的文件部分。 如果能够在 XML 文档中做到这一点就好了。我使用 emacs 22.2.1 和内置的 sgml-mode 进行 xm
已结束。此问题不符合 Stack Overflow guidelines .它目前不接受答案。 我们不允许提出有关书籍、工具、软件库等方面的建议的问题。您可以编辑问题,以便用事实和引用来回答它。 关闭
根据java: public Scanner useDelimiter(String pattern) Sets this scanner's delimiting pattern to a patt
我读过一些关于 PRG 模式以及它如何防止用户重新提交表单的文章。比如this post有一张不错的图: 我能理解为什么在收到 2xx 后用户刷新页面时不会发生表单提交。但我仍然想知道: (1) 如果
看看下面的图片,您可能会清楚地看到这一点。 那么如何在带有其他一些 View 的简单屏幕中实现没有任何弹出/对话框/模式的微调器日期选择器? 我在整个网络上进行了谷歌搜索,但没有找到与之相关的任何合适
我不知道该怎么做,我一直遇到问题。 以下是代码: rows = int(input()) for i in range(1,rows): for j in range(1,i+1):
我想为重写创建一个正则表达式。 将所有请求重写为 index.php(不需要匹配),它不是以/api 开头,或者不是以('.html',或'.js'或'.css'或'.png'结束) 我的例子还是这样
MVC模式代表 Model-View-Controller(模型-视图-控制器) 模式 MVC模式用于应用程序的分层开发 Model(模型) - 模型代表一个存取数据的对象或 JAVA PO
我想为组织模式创建一个 RDF 模式世界。您可能知道,组织模式文档基于层次结构大纲,其中标题是主要的分组实体。 * March auxiliary :PROPERTIES: :HLEVEL: 1 :E
我正在编写一个可以从文件中读取 JSON 数据的软件。该文件包含“person”——一个值为对象数组的对象。我打算使用 JSON 模式验证库来验证内容,而不是自己编写代码。符合代表以下数据的 JSON
假设我有 4 张 table 人 公司 团体 和 账单 现在bills/persons和bills/companys和bills/groups之间是多对多的关系。 我看到了 4 种可能的 sql 模式
假设您有这样的文档: doc1: id:1 text: ... references: Journal1, 2013, pag 123 references: Journal2, 2014,
我有这个架构。它检查评论,目前工作正常。 var schema = { id: '', type: 'object', additionalProperties: false, pro
这可能很简单,但有人可以解释为什么以下模式匹配不明智吗?它说其他规则,例如1, 0, _ 永远不会匹配。 let matchTest(n : int) = let ran = new Rand
我有以下选择序列作为 XML 模式的一部分。理想情况下,我想要一个序列: 来自 my:namespace 的元素必须严格解析。 来自任何其他命名空间的元素,不包括 ##targetNamespace和
我希望编写一个 json 模式来涵盖这个(简化的)示例 { "errorMessage": "", "nbRunningQueries": 0, "isError": Fals
首先,我是 f# 的新手,所以也许答案很明显,但我没有看到。所以我有一些带有 id 和值的元组。我知道我正在寻找的 id,我想从我传入的三个元组中选择正确的元组。我打算用两个 match 语句来做到这
我是一名优秀的程序员,十分优秀!