Golang中正则表达式模式下划线位置对结果的影响

Golang中正则表达式模式下划线位置对结果的影响 我正在尝试验证电子邮件地址,并希望拒绝输入字符串中的分号。但我的测试却通过了。我调整了正则表达式中下划线字符的位置,然后测试就失败了。

有人知道原因吗?

Playground链接:https://play.golang.org/p/xkL9MlFja3_t

package main

import (
	"log"
	"regexp"
)

func main() {
	testString := "a;"
	m, e := regexp.MatchString("^[a-zA-Z0-9.-_]+$", testString)
	if e != nil {
		log.Println(e.Error())
	}
	log.Println("tested:", testString, m) // true
	
	// underscore is no longer the last char in pattern
	m, e = regexp.MatchString("^[a-zA-Z0-9._-]+$", testString)
	if e != nil {
		log.Println(e.Error())
	}
	log.Println("tested:", testString, m) // false
}

更多关于Golang中正则表达式模式下划线位置对结果的影响的实战教程也可以访问 https://www.itying.com/category-94-b0.html

5 回复

啊,谢谢。是的,我没想到那个‘-’表示一个范围。我尝试用逗号转义点号,但也没意识到我需要两个反斜杠。

谢谢两位。

更多关于Golang中正则表达式模式下划线位置对结果的影响的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


当然,我现在才意识到,0-9和A-Z已经告诉了我需要知道的一切! 表情符号

你好,道格,

在第一个例子中,你实际上列出了从 ._ 的所有标点符号,其中包含了 -,因此你测试字符串中的 ; 条件也因此被判定为真。

另一方面,在你的第二个例子中,你并没有列出所有标点符号,而只给出了针对点号、下划线或破折号的条件。所以你的分号没有匹配上,因此被判定为假。

.- 在正则表达式中具有特殊含义。如果你需要字面意义上的 .- 字符,请使用 \ 对它们进行转义:"^[a-zA-Z0-9\\.\\-_]+$"

// 示例代码:使用转义字符的正则表达式
package main

import (
    "fmt"
    "regexp"
)

func main() {
    // 匹配包含字母、数字、点、连字符和下划线的字符串
    pattern := `^[a-zA-Z0-9\.\-_]+$`
    re := regexp.MustCompile(pattern)

    testStrings := []string{"example-123.test", "invalid@char", "simple"}

    for _, str := range testStrings {
        if re.MatchString(str) {
            fmt.Printf("'%s' 匹配成功\n", str)
        } else {
            fmt.Printf("'%s' 匹配失败\n", str)
        }
    }
}

在Go的正则表达式中,字符类([])内的连字符(-)位置会影响其含义。当连字符不在字符类的开头或结尾时,它表示一个范围(如a-z)。

在你的第一个正则表达式 ^[a-zA-Z0-9.-_]+$ 中:

  • .-_ 被解释为从._的字符范围
  • 在ASCII表中,.的码点是46,_的码点是95
  • 分号;的码点是59,正好在这个范围内
  • 因此a;匹配成功

在第二个正则表达式 ^[a-zA-Z0-9._-]+$ 中:

  • ._- 被解释为从.-的字符范围
  • -的码点是45,小于.的码点46
  • Go的正则引擎会拒绝这种无效的范围(起始码点大于结束码点)
  • 整个字符类变为无效,导致匹配失败

正确的做法是将连字符放在字符类的开头或结尾,或者进行转义:

package main

import (
	"log"
	"regexp"
)

func main() {
	testString := "a;"
	
	// 方法1:连字符在开头
	m1, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", testString)
	log.Println("方法1:", testString, m1) // false
	
	// 方法2:连字符在结尾
	m2, _ := regexp.MatchString("^[a-zA-Z0-9._-]+$", testString)
	log.Println("方法2:", testString, m2) // false
	
	// 方法3:转义连字符
	m3, _ := regexp.MatchString(`^[a-zA-Z0-9._\-]+$`, testString)
	log.Println("方法3:", testString, m3) // false
	
	// 验证正常邮箱
	validEmail := "test.user@example.com"
	m4, _ := regexp.MatchString("^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$", validEmail)
	log.Println("有效邮箱:", validEmail, m4) // true
}

对于电子邮件验证,建议使用更完整的正则表达式:

func isValidEmail(email string) bool {
    // RFC 5322标准的简化版本
    pattern := `^[a-zA-Z0-9.!#$%&'*+/=?^_` + "`" + `{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$`
    matched, err := regexp.MatchString(pattern, email)
    return err == nil && matched
}

或者使用Go的net/mail包进行更可靠的验证:

import "net/mail"

func isValidEmail(email string) bool {
    _, err := mail.ParseAddress(email)
    return err == nil
}
回到顶部