6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Goで[]byteをターミナルで表示可能な感じでエスケープする

Last updated at Posted at 2019-02-11

HTMLのテンプレートだと、HTMLセーフな感じで出力するじゃないですか。あれのターミナル版が欲しいと思ったわけですよ。バイナリデータが入ってきたときにそのまま出してしまうとたまにおかしくなってしまったりするし。かといって、テキストなのに全部バイナリにしてしまうと可読性とか困りますよね。一般人は脳内UTF-8デコードとかできませんし。

で、Goの標準ライブラリにはunicode.IsPrint()関数があって、表示できるか判定ができるので、これを利用します。これの元ネタ?のC/C++のctype.hヘッダーのisprint()関数はASCIIの範囲のみで日本語とかは判定できませんが、Goの方はOKのようです。

import (
	"fmt"
	"strings"
	"unicode"
	"unicode/utf8"
)


func Printable(b []byte) string {
	var result strings.Builder
	for len(b) > 0 {
		if utf8.RuneStart(b[0]) {
			r, size := utf8.DecodeRune(b)
			if unicode.IsPrint(r) {
				result.WriteRune(r)
			} else if r == 0x0a {
				result.WriteString("\\n")
			} else {
				fmt.Fprintf(&result, "\\x%02x", r)
			}
			b = b[size:]

		} else {
			fmt.Fprintf(&result, "\\x%02x", b[0])
			b = b[1:]
		}
	}
	return result.String()
}

やっていることは、まずバイト列からRuneを取り出します。その後、 IsPrint() を見て、表示可能ならそのまま文字列にし、だめな場合はエスケープします。ただし、改行だけはよく出てくるので \n 表記に特別扱いしています。

こんなかんじでいいんですかね?非可逆を狙うなら、 \\\ とかにするなど、さらにエスケープが必要な気がしますが。

unicode.IsPrint() の存在を教えてくれた @moriyoshi に感謝です。

2/12修正

utf8.DecodeRune(b) は不正なコードがあると、不正なコードを表すマーカーの文字である0xfffdに変換してしまうようです。stringのrangeループでruneを取り出すときも、この関数が使われます。

ということで、↑のコードを少し修正して、 utf8.RuneStart() でutf-8として正常な文字でないかどうかの判定を追加しています。ただ、これでも、1バイト目は正しいのに2バイト目がダメ、なケースは救えないと思うので、まだ修正が必要です。

6
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?