米琪卡哇伊
前言:我在 中发布了此实用程序(2.Fast 解决方案)github.com/icza/gox,请参阅colorx.ParseHexColor()。1.优雅的解决方案这是另一个使用fmt.Sscanf(). 它当然不是最快的解决方案,但它很优雅。它直接扫描到结构的字段中color.RGBA:func ParseHexColor(s string) (c color.RGBA, err error) { c.A = 0xff switch len(s) { case 7: _, err = fmt.Sscanf(s, "#%02x%02x%02x", &c.R, &c.G, &c.B) case 4: _, err = fmt.Sscanf(s, "#%1x%1x%1x", &c.R, &c.G, &c.B) // Double the hex digits: c.R *= 17 c.G *= 17 c.B *= 17 default: err = fmt.Errorf("invalid length, must be 7 or 4") } return}测试它:hexCols := []string{ "#112233", "#123", "#000233", "#023", "invalid", "#abcd", "#-12",}for _, hc := range hexCols { c, err := ParseHexColor(hc) fmt.Printf("%-7s = %3v, %v\n", hc, c, err)}输出(在Go Playground上尝试):#112233 = { 17 34 51 255}, <nil>#123 = { 17 34 51 255}, <nil>#000233 = { 0 2 51 255}, <nil>#023 = { 0 34 51 255}, <nil>invalid = { 0 0 0 255}, input does not match format#abcd = { 0 0 0 255}, invalid length, must be 7 or 4#-12 = { 0 0 0 255}, expected integer2.快速解决如果性能确实很重要,fmt.Sscanf()那是一个非常糟糕的选择。它需要一个实现必须解析的格式字符串,并根据它解析输入,并使用反射将结果存储到指向的值中。由于任务基本上只是“解析”一个十六进制值,我们可以做得更好。我们甚至不必调用通用的十六进制解析库(例如encoding/hex),我们可以自己完成。我们甚至不必将输入视为 a string,甚至不必将其视为一系列runes,我们可能会降低到将其视为一系列字节的级别。是的,Go 在内存中将值存储为 UTF-8 字节序列,但如果输入是有效的颜色字符串,则其所有字节必须在1 对 1 映射到字节string的范围内。0..127如果不是这种情况,输入将已经无效,我们将检测到这一点,但在这种情况下我们返回什么颜色应该无关紧要(无关紧要)。现在让我们看一个快速的实现:var errInvalidFormat = errors.New("invalid format")func ParseHexColorFast(s string) (c color.RGBA, err error) { c.A = 0xff if s[0] != '#' { return c, errInvalidFormat } hexToByte := func(b byte) byte { switch { case b >= '0' && b <= '9': return b - '0' case b >= 'a' && b <= 'f': return b - 'a' + 10 case b >= 'A' && b <= 'F': return b - 'A' + 10 } err = errInvalidFormat return 0 } switch len(s) { case 7: c.R = hexToByte(s[1])<<4 + hexToByte(s[2]) c.G = hexToByte(s[3])<<4 + hexToByte(s[4]) c.B = hexToByte(s[5])<<4 + hexToByte(s[6]) case 4: c.R = hexToByte(s[1]) * 17 c.G = hexToByte(s[2]) * 17 c.B = hexToByte(s[3]) * 17 default: err = errInvalidFormat } return}使用与第一个示例相同的输入对其进行测试,输出为(在Go Playground上尝试):#112233 = { 17 34 51 255}, <nil>#123 = { 17 34 51 255}, <nil>#000233 = { 0 2 51 255}, <nil>#023 = { 0 34 51 255}, <nil>invalid = { 0 0 0 255}, invalid format#abcd = { 0 0 0 255}, invalid format#-12 = { 0 17 34 255}, invalid format3. 基准让我们对这两个解决方案进行基准测试。基准测试代码将包括使用长格式和短格式调用它们。排除错误情况。func BenchmarkParseHexColor(b *testing.B) { for i := 0; i < b.N; i++ { ParseHexColor("#112233") ParseHexColor("#123") }}func BenchmarkParseHexColorFast(b *testing.B) { for i := 0; i < b.N; i++ { ParseHexColorFast("#112233") ParseHexColorFast("#123") }}以下是基准测试结果:go test -bench . -benchmemBenchmarkParseHexColor-4 500000 2557 ns/op 144 B/op 9 allocs/opBenchmarkParseHexColorFast-4 100000000 10.3 ns/op 0 B/op 0 allocs/op正如我们所见,“快速”解决方案大约快250 倍并且不使用分配(与“优雅”解决方案不同)。
MMMHUHU
您可以使用将任意 2 个十六进制数字转换为整数strconv.ParseUintstrconv.ParseUint(str, 16, 8)指示16基数 16(十六进制),8 指示位数,在本例中为一个字节。您可以使用它来将每 2 个字符解析为它们的组件https://play.golang.org/p/B56B8_NvnVRfunc ParseHexColor(v string) (out color.RGBA, err error) { if len(v) != 7 { return out, errors.New("hex color must be 7 characters") } if v[0] != '#' { return out, errors.New("hex color must start with '#'") } var red, redError = strconv.ParseUint(v[1:3], 16, 8) if redError != nil { return out, errors.New("red component invalid") } out.R = uint8(red) var green, greenError = strconv.ParseUint(v[3:5], 16, 8) if greenError != nil { return out, errors.New("green component invalid") } out.G = uint8(green) var blue, blueError = strconv.ParseUint(v[5:7], 16, 8) if blueError != nil { return out, errors.New("blue component invalid") } out.B = uint8(blue) return}