Golang中如何指定字符串类型/格式

Golang中如何指定字符串类型/格式 你好,我正在创建一个程序,它接收用户输入的主机名。如你所知,主机名的格式类似于 server.hostname.com

因此,我希望在主机名无效时程序能退出。

例如,如果客户输入了 1hellodomain.comexample.com,程序将退出;而如果客户输入了正确的主机名,如 server.server.comvps.mydomain.com 等,程序将输出正确。

对此有什么想法吗?

3 回复

在什么确切条件之后才有效?hello 对我来说看起来像是一个正确的主机名……同样,example.com 也是如此……

更多关于Golang中如何指定字符串类型/格式的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


我不太清楚你所说的“有效”具体指什么。我认为你可能对主机名的概念有些困惑。在你的例子中,看起来你可能是想要“包含子域名、二级域名和顶级域名”。一般来说,我认为 net/url 包是你需要的。下面的代码:

package main

import (
	"fmt"
	"log"
	"net/url"
	"strings"
)

func main() {
	testCases := []string{"server.hostname.com", "server.hostname.com:443/?q=asdf", "1", "hello", "domain.com", "123.asd.xyz", "has space in it"}
	for _, h := range testCases {
		validateHost(h)
		validateSubdomain(h)
	}
}

func validateHost(hn string) {
	u := parseURI(hn)
	if len(u.Host) > 0 {
		// 可以尝试改为 u.Hostname() 来去除端口号...
		fmt.Println(u.Host, "is a valid host name")
	}
}

func validateSubdomain(hn string) {
	u := parseURI(hn)
	if strings.Count(u.Host, ".") == 2 {
		fmt.Println(u.Host, "contains sub, second-level, and top level domain")
	}
}

func parseURI(hn string) *url.URL {
	// 将方案硬编码为 https
	u, err := url.Parse("https://" + hn)
	if err != nil {
		log.Fatal(err)
	}
	return u
}

https://play.golang.org/p/y0Hr43XteiN

… 将会得到:

server.hostname.com is a valid host name
server.hostname.com contains sub, second-level, and top level domain
server.hostname.com:443 is a valid host name
server.hostname.com:443 contains sub, second-level, and top level domain
1 is a valid host name
hello is a valid host name
domain.com is a valid host name
123.asd.xyz is a valid host name
123.asd.xyz contains sub, second-level, and top level domain
2009/11/10 23:00:00 parse "https://has space in it": invalid character " " in host name

… 这应该足够帮助你开始了。

在Go中,可以通过正则表达式验证主机名格式。以下是一个示例实现:

package main

import (
    "fmt"
    "os"
    "regexp"
)

func isValidHostname(hostname string) bool {
    // 主机名正则规则:
    // 1. 由点分隔的多个标签组成
    // 2. 每个标签以字母或数字开头和结尾
    // 3. 标签内可包含字母、数字、连字符
    // 4. 标签长度1-63字符
    // 5. 总长度不超过253字符
    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][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`
    
    if len(hostname) > 253 {
        return false
    }
    
    matched, err := regexp.MatchString(pattern, hostname)
    if err != nil {
        return false
    }
    
    return matched
}

func main() {
    // 测试用例
    testCases := []string{
        "1",
        "hello",
        "domain.com",
        "example.com",
        "server.server.com",
        "vps.mydomain.com",
        "valid-host.example.com",
        "-invalid.com",
        "invalid-.com",
        "this-label-is-way-too-long-for-a-hostname-label.example.com",
    }
    
    for _, hostname := range testCases {
        if isValidHostname(hostname) {
            fmt.Printf("✓ 有效: %s\n", hostname)
        } else {
            fmt.Printf("✗ 无效: %s\n", hostname)
        }
    }
    
    // 实际使用示例
    var input string
    fmt.Print("请输入主机名: ")
    fmt.Scanln(&input)
    
    if !isValidHostname(input) {
        fmt.Println("错误: 无效的主机名格式")
        os.Exit(1)
    }
    
    fmt.Printf("主机名 %s 有效\n", input)
}

对于更严格的验证,可以使用net包的内置函数:

package main

import (
    "fmt"
    "net"
    "os"
    "strings"
)

func isValidHostnameStrict(hostname string) bool {
    // 移除可能的端口号
    hostname = strings.Split(hostname, ":")[0]
    
    // 使用net.LookupHost验证
    _, err := net.LookupHost(hostname)
    if err != nil {
        // 尝试作为域名解析
        _, err = net.LookupAddr(hostname)
        return err == nil
    }
    return true
}

func main() {
    hostnames := []string{
        "google.com",
        "invalid-hostname-12345.com",
        "localhost",
        "192.168.1.1",
    }
    
    for _, hostname := range hostnames {
        if isValidHostnameStrict(hostname) {
            fmt.Printf("可解析: %s\n", hostname)
        } else {
            fmt.Printf("不可解析: %s\n", hostname)
        }
    }
}

如果需要同时验证格式和解析性:

package main

import (
    "fmt"
    "net"
    "os"
    "regexp"
    "strings"
)

func validateHostname(hostname string) error {
    // 基础格式验证
    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][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`
    
    if len(hostname) > 253 {
        return fmt.Errorf("主机名长度超过253字符")
    }
    
    matched, _ := regexp.MatchString(pattern, hostname)
    if !matched {
        return fmt.Errorf("主机名格式无效")
    }
    
    // 尝试解析
    hostname = strings.Split(hostname, ":")[0]
    _, err := net.LookupHost(hostname)
    if err != nil {
        return fmt.Errorf("主机名无法解析: %v", err)
    }
    
    return nil
}

func main() {
    var input string
    fmt.Print("输入主机名: ")
    fmt.Scanln(&input)
    
    if err := validateHostname(input); err != nil {
        fmt.Printf("错误: %v\n", err)
        os.Exit(1)
    }
    
    fmt.Println("主机名验证通过")
}

这些示例提供了不同严格程度的验证方法,可根据实际需求选择使用。

回到顶部