我需要決議可能無效或包含一些錯誤的 UDP 資料包。我想用.位元組到字串轉換后替換無效字符,以顯示資料包的內容。
我該怎么做?這是我的代碼:
func main() {
a := []byte{'a', 0xff, 0xaf, 'b', 0xbf}
s := string(a)
s = strings.Replace(s, string(0xFFFD), ".", 0)
fmt.Println("s: ", s) // I would like to display "a..b."
for _, r := range s {
fmt.Println("r: ", r)
}
rs := []rune(s)
fmt.Println("rs: ", rs)
}
uj5u.com熱心網友回復:
您的方法的根本問題是型別轉換[]byte為的結果string中沒有任何U FFFDs :此型別轉換僅將位元組從源逐字復制到目標。
就像位元組切片一樣,Go 中的字串沒有義務包含 UTF-8 編碼的文本;它們可以包含任何資料,包括與文本無關的不透明二進制資料。
但是對字串的一些操作——即將它們型別轉換為[]rune并使用range它們迭代——確實將字串解釋為 UTF-8 編碼的文本。這正是你被絆倒的地方:你的range除錯回圈試圖解釋字串,每次解碼正確編碼的代碼點的嘗試失敗時,都會range產生一個替換字符,U FFFD.
重申一下,通過型別轉換獲得的字串不包含您希望被正則運算式替換的字符。
至于如何從您的資料中實際生成有效的 UTF-8 編碼字串,您可以采用兩步程序:
- 將你的位元組切片型別轉換為字串——就像你已經做的那樣。
- 使用任何將字串解釋為 UTF-8 的方法——替換將在此程序中動態出現的 U FFFD——在您進行迭代時。
像這樣的東西:
var sb strings.Builder
for _, c := range string(b) {
if c == '\uFFFD' {
sb.WriteByte('.')
} else {
sb.WriteRune(c)
}
}
return sb.String()
關于性能的注意事項:由于將 a 型別轉換[]byte為string復制記憶體——因為字串是不可變的,而切片不是——型別轉換的第一步可能會浪費處理大量資料和/或緊密作業的代碼的資源處理回圈。
在這種情況下,可能值得使用適用于位元組片DecodeRune的encoding/utf8包的功能。它的檔案中的一個例子可以很容易地適應上面的回圈。
另請參閱:從字串中洗掉無效的 UTF-8 字符
uj5u.com熱心網友回復:
@kostix 答案是正確的,并且非常清楚地解釋了從字串中掃描 unicode 符文的問題。
只需添加以下注釋:如果您的意圖是僅查看 ASCII 范圍內的字符(可列印字符 < 127)并且您并不真正關心其他 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。
uj5u.com熱心網友回復:
您可能希望strings.ToValidUTF8()用于此:
ToValidUTF8 回傳字串 s 的副本,其中每次運行無效的 UTF-8 位元組序列都被替換字串替換,該替換字串可能為空。
它“似乎”完全符合您的需要。測驗它:
a := []byte{'a', 0xff, 0xaf, 'b', 0xbf}
s := strings.ToValidUTF8(string(a), ".")
fmt.Println(s)
輸出(在Go Playground上試試):
a.b.
我寫“貌似”是因為如您所見,a和b:之間只有一個點,因為可能有 2 個位元組,但有一個無效序列。
請注意,您可以避免[]byte=>string轉換,因為有一個bytes.ToValidUTF8()等價的操作并回傳 a []byte:
a := []byte{'a', 0xff, 0xaf, 'b', 0xbf}
a = bytes.ToValidUTF8(a, []byte{'.'})
fmt.Println(string(a))
輸出將是相同的。在Go Playground上試試這個。
如果您對多個(無效序列)位元組可能會縮小為一個點感到困擾,請繼續閱讀。
另請注意,要檢查可能包含或不包含文本的任意位元組切片,您可以簡單地使用hex.Dump()which 生成如下輸出:
a := []byte{'a', 0xff, 0xaf, 'b', 0xbf}
fmt.Println(hex.Dump(a))
輸出:
00000000 61 ff af 62 bf |a..b.|
您的預期輸出a..b.包含其他(有用的)資料,例如十六進制偏移量和位元組的十六進制表示。
要獲得“更好”的輸出圖片,請嘗試使用更長的輸入:
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 |...|
在Go Playground上嘗試一下。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/409648.html
標籤:
