【问题标题】:What is an efficient way of doing URL matching and tag extraction?进行 URL 匹配和标签提取的有效方法是什么?
【发布时间】:2012-04-15 14:56:27
【问题描述】:

鉴于a = "/some/{tag}/here"b = "/some/text/here" 这两个字符串,我想要一个有效的算法来验证b 是否与a 定义的模式匹配,以及是否匹配在变量中提取b 的对应部分(即:tag = "text")。

也欢迎使用 C 或 Go 实现,但伪代码也可以。

【问题讨论】:

  • {pattern} 有多复杂?通常,人们会使用正则表达式来处理此类事情。
  • {pattern} 只是一个标签。命名确实令人困惑,“模式”由a 表示,而不是{pattern}。我相应地编辑了问题。
  • 目前还不清楚您在寻找什么。如果a="/some/{tag}/hereb="/this/example/text",是否匹配并且您想提取字符串example?还是 b 必须是 `"/some/example/here"?
  • {} 是在 atag 的其他地方不存在的实际触发器是纯文本吗? (没有正则表达式等) - a 是在一个操作中重复使用多次(从多个 d 中提取)还是使用一次然后使用新模式,返回结果或...
  • 没错,{} 是触发器,标签总是会被这些字符包裹起来,而标签是纯文本,会被用作变量名。我想用它来从 URL 中捕获参数。例如,如果我为"/some/{tag}/here" 模式注册了一个处理程序并且用户导航到"/some/candy/here",我希望能够判断用户是否导航到有效路径,如果是,则从中提取标签,所以在这个从 URL 中提取 "candy" 的特殊情况。

标签: c string algorithm pattern-matching go


【解决方案1】:

了解the Knuth–Morris–Pratt string searching algorithm。应该给你所有你需要的东西,包括伪代码。

【讨论】:

  • 我知道 Knuth-Morris-Pratt 算法,但我不知道它是否适合我的用例。您在推荐时是否考虑了用例?谢谢!
  • 是的,KMP 非常好,通常用于在文件中查找模式。在您的情况下,由于您只有一条线(您可以通过标记化进一步简化),KMP 将创造奇迹。此外,如果您有多个要比较的texts,但并非所有都不同,您可以使用HashMap 来存储已经找到的tags,并首先检查它是否在地图上可用。这样您就可以提高算法的效率。祝你好运!!
【解决方案2】:

许多好的正则表达式工具包都可以做到这一点,但您可能必须更改模式的语法。例如,这里是 Python 版本:

>>> import re
>>> a = re.compile("/some/(?P<pattern>.+)/here")
>>> b = "/some/text/here"
>>> a.match(b).group("pattern")
'text'

【讨论】:

  • 谢谢,这就是我想要实现的目标,我更喜欢非正则表达式解决方案,但如果我找不到其他方法,我会回退到此。
【解决方案3】:

也许你可以拆分a

string[] array1 = a.Split('/');
string[] array2 = a.Split('/');
bool isEqual = (array1[2] == array2[2]);

【讨论】:

  • 这是部分解决方案。你应该完成它。
【解决方案4】:

所以你有一个/some/{tag}/here 形式的模式字符串,并且你想确定其他字符串是否与该模式匹配。如果是,那么您要提取 {tag} 部分。

在我看来,您可以将模式字符串分成三部分:

"/some/"
"{tag}"
"/here"

现在,使用标准 C 比较函数(我在想类似 strncmp 的东西),检查字符串是否以 "/some/" 开头并以 "/here" 结尾。如果是这样,那么您可以轻松找到标记字符串的开头和结尾。开头是:

stringBegin = s + strlen("/some/");
length = strlen(s) - strlen("/some/") - strlen("/here");

那么复制那个子字符串就很简单了。

当然,我的示例是使用常量字符串。但是,如果您可以轻松拆分组件,那么您可以用变量替换常量。

【讨论】:

  • 谢谢,这是一个巧妙的方法。但是,如果模式包含多个标签,我不确定它是否会起作用。
  • 如果模式有多个标签,同样的方法也适用,前提是您可以识别标签的位置。如果您可以解析出已知部分,那么剩下的就是标签。如果模式可以是"/some/{tag1}/text/{tag2}/here" 的形式,那么您还需要使用strstr 函数。如果您的模式比这复杂得多,那么您最好找到一个正则表达式库并学习如何使用它。
【解决方案5】:

Go 回答:Go 标准库有一个 URL parserregular expression 包来帮助你。 Go 不允许您在运行时命名变量,因此将您的答案设为 tag = "text" 并没有多大意义。相反,您可能希望将结果作为结构返回,或者可能在映射中收集多个结果。大纲可能类似于,

  1. 编译一个正则表达式,它与您的标记语法与大括号相匹配。您在程序加载时执行一次。让我们称之为 tagRE。
  2. 将 tagRE 应用于模式“a”。此匹配的结果将是要匹配的 URL 部分以及标记的名称。 (如果匹配失败,模式“a”无效。)
  3. 使用结果构造和编译另一个与真实 url 中的模式匹配的正则表达式。让我们称之为aRE。只要您认为将来可能需要匹配此模式,请坚持使用此正则表达式。重复编译工作是没有意义的。
  4. 可以根据需要对其他模式重复步骤 2 和 3,或者当模式可用于您的程序时。也许将这些收集在切片或地图或其他东西中。我猜您还希望将这些与您的应用程序中其他有用的东西相关联,例如找到匹配项时要执行的一些代码。
  5. 当你有一个真实的 url 想要匹配时,你可能想先用 URL 包解析它以分离出 URL 路径。
  6. 将 aRE(或切片中的所有正则表达式)应用于路径并查看是否有匹配项。如果是这样,则返回一个结果,其中包含来自 a 的标记名称和匹配的路径部分。您可以通过创建结果结构或添加到结果映射来做到这一点。

显示正则表达式构造的代码:

package main

import (
    "fmt"
    "regexp"
)

var a = "/some/{tag}/here/{and}/there"
var aPath = `/some/bread/here/jam/there`

func main() {
    tagPat := regexp.MustCompile("([^{]*){([^}]+)}")
    aMatch := tagPat.FindAllStringSubmatch(a, -1)
    if aMatch == nil {
        fmt.Println("bad pattern")
        return
    }
    aRE := ""
    matchLen := 0
    for _, m := range aMatch {
        if m[1] > "" {
            aRE += `\Q` + m[1] + `\E`
        }
        aRE += "(?P<" + m[2] + ">.*)"
        matchLen += len(m[0])
    }
    if matchLen < len(a) {
        aRE += `\Q` + a[matchLen:] + `\E`
    }
    aPat := regexp.MustCompile(aRE)
    pathMatch := aPat.FindStringSubmatch(aPath)
    if pathMatch == nil {
        fmt.Println("url doesn't match")
        return
    }
    for tx, tag := range aPat.SubexpNames()[1:] {
        fmt.Println(tag, "=", pathMatch[tx+1])
    }
}

输出:

标签=面包
和=果酱

【讨论】:

  • 我不想在运行时创建变量。我可以使用地图来存储标签及其值。
  • 好。再想一想,虽然它不是原始问题的答案,但看起来您正在尝试做通常使用 http 包中的 HandleFunc 完成的事情。如果您还没有,请参阅Writing Web Applications
  • 谢谢,是的,您对 http 包的看法是正确的,但是它不支持我想使用 {tag} 实现的参数提取。此外,我对算法比对图书馆的实际解决方案更感兴趣。如果 http 包支持它,那将非常有趣,因为我将能够分析它的实现。
  • Gorilla 的 mux 包可能会满足您的需求。抱歉,移动设备上没有链接,但它是 Google 上的第一页。
  • 谢谢,看看它的实现。
【解决方案6】:

我假设您的标签中不能有斜线。如果不是这样,我的解决方案将无法正常工作 相当大的修改。

如果上述情况成立,那么您可以首先将您的路径标记为一个列表,如他的答案中显示的 user1288160。我的解决方案即将推出。

path := strings.Split(url, "/")

然后您可以使用简单的状态机来处理令牌。

type urlParser func([]string) (urlParser, []string, error)

// define handlers for the various tokens that do appropriate things
var parseMap map[string]urlParser

var startParse = func(ps []string) (urlParser, []string, error) {
   switch  {
   case len(ps) == 0:
      return nil, nil, errors.New("End Of Path")
   case len(ps) == 1:
     return parseMap[ps[0]], nil, nil
   case len(ps) > 1:
     return parseMap[ps[0]], ps[1:], nil
   }
}

p := startParse
var err error
for {
   // get the next step in the state machine, unparsed portion of the path
   // and any errors.
   next, rst, pErr := p(path)
   // an error means we are done.
   if pErr != nil {
     break;
   }
   // set up for our next iteration of the parse loop.
   p = next
   path = rst
   err = pErr
}

你的 urlParsers 将是用你匹配的任何东西填充一些变量的闭包。

【讨论】:

    【解决方案7】:

    为了我们能帮上忙,我们需要背景信息。例如,什么构成了“模式”,数字?字母?数字和字母?允许使用哪些字符?

    第一景:假设路径目标的位置是固定的,你可以这样做:

    C 代码:

    char * string = "/some/text/here";
    char * path;
    char * b = "text";
    
    if(strtok(strdup(string), "/")) {
        path = strtok(NULL, "/");
        if(!strcmp(b, path)) {
            /* Are equals. Do something.. */
        } else {
            /* ... */
        }
    } else { 
        printf("Not found tag.\n");
    }
    

    第二幅风景:

    假设你只知道路径目标的前身,你可以这样做:

    C 代码:

    char * string = "/some/text/here";
    
    char *cpath,            /* Current path */ 
         *ppath   = NULL,   /* Predecessor path */
         *ptpath  = "some", /* Predecessor path target */
         *pathcmp = "text"; /* Path to compare */ 
    
    cpath = strtok(strdup(string), "/");
    
     while(cpath) { 
        ppath = cpath; 
        cpath = strtok(NULL, "/");
    
        if(ppath && ptpath && !strcmp(ppath, ptpath)) {
            if(!strcmp(cpath, pathcmp)) {
                /* Are equals. */
            } else {
                /* ... */
            }
    
            break;
        }
    }
    

    像这样的非常简单的情况,可以从正则表达式和 URI 解析中逃脱(当然,在良好的意义上)。

    希望对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-28
      • 1970-01-01
      • 1970-01-01
      • 2015-07-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多