3 回答

TA貢獻1998條經驗 獲得超6個贊
前言:我在 中發布了此實用程序(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 integer
2.快速解決
如果性能確實很重要,fmt.Sscanf()
那是一個非常糟糕的選擇。它需要一個實現必須解析的格式字符串,并根據它解析輸入,并使用反射將結果存儲到指向的值中。
由于任務基本上只是“解析”一個十六進制值,我們可以做得更好。我們甚至不必調用通用的十六進制解析庫(例如encoding/hex
),我們可以自己完成。我們甚至不必將輸入視為 a?string
,甚至不必將其視為一系列rune
s,我們可能會降低到將其視為一系列字節的級別。是的,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 format
3. 基準
讓我們對這兩個解決方案進行基準測試。基準測試代碼將包括使用長格式和短格式調用它們。排除錯誤情況。
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 . -benchmem
BenchmarkParseHexColor-4? ? ? ? ?500000? ? ?2557 ns/op? ? ? 144 B/op? ? 9 allocs/op
BenchmarkParseHexColorFast-4? ?100000000? ? ? 10.3 ns/op? ? ? 0 B/op? ? 0 allocs/op
正如我們所見,“快速”解決方案大約快250 倍并且不使用分配(與“優雅”解決方案不同)。

TA貢獻1808條經驗 獲得超4個贊
RGBA 顏色只有 4 個字節,紅色、綠色、藍色和 alpha 通道各一個。對于三個或六個十六進制數字,字母字節通常隱含為 0xFF(AABBCC被認為與 一樣AABBCCFF,按原樣ABC)。
因此解析顏色字符串就像對其進行規范化一樣簡單,使其具有“RRGGBBAA”(4 個十六進制編碼字節)的形式,然后對其進行解碼:
package main
import (
"encoding/hex"
"fmt"
"image/color"
"log"
)
func main() {
colorStr := "102030FF"
colorStr, err := normalize(colorStr)
if err != nil {
log.Fatal(err)
}
b, err := hex.DecodeString(colorStr)
if err != nil {
log.Fatal(err)
}
color := color.RGBA{b[0], b[1], b[2], b[3]}
fmt.Println(color) // Output: {16 32 48 255}
}
func normalize(colorStr string) (string, error) {
// left as an exercise for the reader
return colorStr, nil
}
在操場上試試:https ://play.golang.org/p/aCX-vyfMG4G

TA貢獻1834條經驗 獲得超8個贊
您可以使用將任意 2 個十六進制數字轉換為整數strconv.ParseUint
strconv.ParseUint(str,?16,?8)
指示16
基數 16(十六進制),8 指示位數,在本例中為一個字節。
您可以使用它來將每 2 個字符解析為它們的組件
https://play.golang.org/p/B56B8_NvnVR
func 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
}
- 3 回答
- 0 關注
- 168 瀏覽
添加回答
舉報