3 回答

TA貢獻(xiàn)1712條經(jīng)驗 獲得超3個贊
您的方法的根本問題是類型轉(zhuǎn)換[]byte
為的結(jié)果中string
沒有任何U+FFFD
s :此類型轉(zhuǎn)換僅將字節(jié)從源逐字復(fù)制到目標(biāo)。
就像字節(jié)切片一樣,Go 中的字符串沒有義務(wù)包含 UTF-8 編碼的文本;它們可以包含任何數(shù)據(jù),包括與文本無關(guān)的不透明二進(jìn)制數(shù)據(jù)。
但是對字符串的一些操作——即將它們類型轉(zhuǎn)換為[]rune
并使用它們迭代range
——確實將字符串解釋為 UTF-8 編碼的文本。這正是你被絆倒的地方:你的range
調(diào)試循環(huán)試圖解釋字符串,每次解碼正確編碼的代碼點的嘗試失敗時,都會range
產(chǎn)生一個替換字符,U+FFFD
.
重申一下,通過類型轉(zhuǎn)換獲得的字符串不包含您希望被正則表達(dá)式替換的字符。
至于如何從您的數(shù)據(jù)中實際生成有效的 UTF-8 編碼字符串,您可以采用兩步過程:
將你的字節(jié)切片類型轉(zhuǎn)換為字符串——就像你已經(jīng)做的那樣。
使用任何將字符串解釋為 UTF-8 的方法——替換將在此過程中動態(tài)出現(xiàn)的 U+FFFD——在您進(jìn)行迭代時。
像這樣的東西:
var sb strings.Builder
for _, c := range string(b) {
if c == '\uFFFD' {
sb.WriteByte('.')
} else {
sb.WriteRune(c)
}
}
return sb.String()
關(guān)于性能的說明:由于將 a 類型轉(zhuǎn)換[]byte為string復(fù)制內(nèi)存——因為字符串是不可變的,而切片不是——類型轉(zhuǎn)換的第一步可能會浪費處理大量數(shù)據(jù)和/或緊密工作的代碼的資源處理循環(huán)。
在這種情況下,可能值得使用適用于字節(jié)片的包的DecodeRune
功能。encoding/utf8
它的文檔中的一個例子可以很容易地適應(yīng)上面的循環(huán)。

TA貢獻(xiàn)2003條經(jīng)驗 獲得超2個贊
您可能希望strings.ToValidUTF8()用于此:
ToValidUTF8 返回字符串 s 的副本,其中每次運行無效的 UTF-8 字節(jié)序列都被替換字符串替換,該替換字符串可能為空。
它“似乎”完全符合您的需要。測試它:
a := []byte{'a', 0xff, 0xaf, 'b', 0xbf}
s := strings.ToValidUTF8(string(a), ".")
fmt.Println(s)
輸出(在Go Playground上試試):
a.b.
我寫“貌似”是因為如您所見,a和b: 之間只有一個點,因為可能有 2 個字節(jié),但有一個無效序列。
請注意,您可以避免[]byte=>string轉(zhuǎn)換,因為有一個bytes.ToValidUTF8()等價的操作并返回 a []byte:
a := []byte{'a', 0xff, 0xaf, 'b', 0xbf}
a = bytes.ToValidUTF8(a, []byte{'.'})
fmt.Println(string(a))
輸出將是相同的。在Go Playground上試試這個。
如果您對多個(無效序列)字節(jié)可能會縮小為一個點感到困擾,請繼續(xù)閱讀。
另請注意,要檢查可能包含或不包含文本的任意字節(jié)切片,您可以簡單地使用hex.Dump()which 生成如下輸出:
a := []byte{'a', 0xff, 0xaf, 'b', 0xbf}
fmt.Println(hex.Dump(a))
輸出:
00000000 61 ff af 62 bf |a..b.|
您的預(yù)期輸出a..b.包含其他(有用的)數(shù)據(jù),例如十六進(jìn)制偏移量和字節(jié)的十六進(jìn)制表示。
要獲得“更好”的輸出圖片,請嘗試使用更長的輸入:
a = []byte{'a', 0xff, 0xaf, 'b', 0xbf, 50: 0xff}
fmt.Println(hex.Dump(a))
00000000 61 ff af 62 bf 00 00 00 00 00 00 00 00 00 00 00 |a..b............|
00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000030 00 00 ff |...|

TA貢獻(xiàn)1871條經(jīng)驗 獲得超13個贊
并且非常清楚地解釋了從字符串中掃描 unicode 符文的問題。
只需添加以下注釋:如果您的意圖是僅查看 ASCII 范圍內(nèi)的字符(可打印字符 < 127)并且您并不真正關(guān)心其他 unicode 代碼點,您可以更直率:
// create a byte slice with the same byte length as s
var bs = make([]byte, len(s))
// scan s byte by byte :
for i := 0; i < len(s); i++ {
switch {
case 32 <= s[i] && s[i] <= 126:
bs[i] = s[i]
// depending on your needs, you may also keep characters in the 0..31 range,
// like 'tab' (9), 'linefeed' (10) or 'carriage return' (13) :
// case s[i] == 9, s[i] == 10, s[i] == 13:
// bs[i] = s[i]
default:
bs[i] = '.'
}
}
fmt.Printf("rs: %s\n", bs)
這個功能會給你一些接近“文本”部分的東西hexdump -C。
- 3 回答
- 0 關(guān)注
- 148 瀏覽
添加回答
舉報