3 回答

TA貢獻(xiàn)1998條經(jīng)驗(yàn) 獲得超6個(gè)贊
前言:我在 中發(fā)布了此實(shí)用程序(2.Fast 解決方案)github.com/icza/gox
,請(qǐng)參閱colorx.ParseHexColor()
。
1.優(yōu)雅的解決方案
這是另一個(gè)使用fmt.Sscanf()
.?它當(dāng)然不是最快的解決方案,但它很優(yōu)雅。它直接掃描到結(jié)構(gòu)的字段中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.快速解決
如果性能確實(shí)很重要,fmt.Sscanf()
那是一個(gè)非常糟糕的選擇。它需要一個(gè)實(shí)現(xiàn)必須解析的格式字符串,并根據(jù)它解析輸入,并使用反射將結(jié)果存儲(chǔ)到指向的值中。
由于任務(wù)基本上只是“解析”一個(gè)十六進(jìn)制值,我們可以做得更好。我們甚至不必調(diào)用通用的十六進(jìn)制解析庫(例如encoding/hex
),我們可以自己完成。我們甚至不必將輸入視為 a?string
,甚至不必將其視為一系列rune
s,我們可能會(huì)降低到將其視為一系列字節(jié)的級(jí)別。是的,Go 在內(nèi)存中將值存儲(chǔ)為 UTF-8 字節(jié)序列,但如果輸入是有效的顏色字符串,則其所有字節(jié)必須在1 對(duì) 1 映射到字節(jié)string
的范圍內(nèi)。0..127
如果不是這種情況,輸入將已經(jīng)無效,我們將檢測到這一點(diǎn),但在這種情況下我們返回什么顏色應(yīng)該無關(guān)緊要(無關(guān)緊要)。
現(xiàn)在讓我們看一個(gè)快速的實(shí)現(xiàn):
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
}
使用與第一個(gè)示例相同的輸入對(duì)其進(jìn)行測試,輸出為(在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. 基準(zhǔn)
讓我們對(duì)這兩個(gè)解決方案進(jìn)行基準(zhǔn)測試?;鶞?zhǔn)測試代碼將包括使用長格式和短格式調(diào)用它們。排除錯(cuò)誤情況。
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")
? ? }
}
以下是基準(zhǔn)測試結(jié)果:
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 倍并且不使用分配(與“優(yōu)雅”解決方案不同)。

TA貢獻(xiàn)1808條經(jīng)驗(yàn) 獲得超4個(gè)贊
RGBA 顏色只有 4 個(gè)字節(jié),紅色、綠色、藍(lán)色和 alpha 通道各一個(gè)。對(duì)于三個(gè)或六個(gè)十六進(jìn)制數(shù)字,字母字節(jié)通常隱含為 0xFF(AABBCC被認(rèn)為與 一樣AABBCCFF,按原樣ABC)。
因此解析顏色字符串就像對(duì)其進(jìn)行規(guī)范化一樣簡單,使其具有“RRGGBBAA”(4 個(gè)十六進(jìn)制編碼字節(jié))的形式,然后對(duì)其進(jìn)行解碼:
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貢獻(xiàn)1834條經(jīng)驗(yàn) 獲得超8個(gè)贊
您可以使用將任意 2 個(gè)十六進(jìn)制數(shù)字轉(zhuǎn)換為整數(shù)strconv.ParseUint
strconv.ParseUint(str,?16,?8)
指示16
基數(shù) 16(十六進(jìn)制),8 指示位數(shù),在本例中為一個(gè)字節(jié)。
您可以使用它來將每 2 個(gè)字符解析為它們的組件
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 關(guān)注
- 163 瀏覽
添加回答
舉報(bào)