Skip to content

Go bytesパッケージ Walkthrough

2023/02/08

Go

目次

目次

  1. bytes パッケージについて
  2. Clone
  3. Compare
  4. Contains
    1. Contains
    2. ContainsAny
    3. ContainsRune
    4. Index
    5. IndexAny
    6. IndexByte
    7. IndexRune
  5. Count
  6. Cut
  7. Equal
    1. Equal
    2. EqualFold
  8. Prefix
  9. Join
    1. Join
  10. Index
    1. LastIndex
  11. Repeat/Replace/Split
    1. Repeat
    2. Replace
    3. ReplaceAll
    4. Runes
    5. Split
  12. Lower/Upper
    1. ToLower
  13. Trim
    1. Trim
  14. Buffer

bytes パッケージについて

bytes package はバイトスライスを操作するための関数を提供します。

$ gocloc --not-match=test bytes
-------------------------------------------------------------------------------
Language                     files          blank        comment           code
-------------------------------------------------------------------------------
Go                               3            149            459           1405
-------------------------------------------------------------------------------
TOTAL                            3            149            459           1405
-------------------------------------------------------------------------------

It is analogous to the facilities of the strings package.
(go doc)

bytes/strings は類似しており、かつ多くの便利メソッドが提供されています。
メソッド内の実装について触れつつ、どのような便利メソッドが提供されているか見ていきます。

前提知識として、Go における文字列、バイト、ルーンの取り扱いについてはこちらの記事がとてもわかりやすいです。
Strings, bytes, runes and characters in Go - The Go Programming Language

また、Go では文字列・バイト・ルーンを相互に変換可能です。
The Go Programming Language Specification - The Go Programming Language

Clone

func Clone(b []byte) []byte

Go 1.20 から追加されました。引数に渡されたバイトスライスのコピーを返します。

もとのバイトスライスへ影響を与えず操作したい場合に利用する便利メソッドです。

Compare

2 つのバイトスライスの並び替えの順番を決める場合などに利用できます。

func Compare(a, b []byte) int

2 つのバイトスライスを比較し、a==b 場合は 0、a<b の場合は-1、a>b の場合は 1 を返します。

アセンブリで実装されたinternal/bytealgのメソッドが呼ばれます。

Contains

内部ではIndex系の関数が呼ばれる便利メソッドです。

特定のバイトスライスが存在するか検証します。何らかの入力に対するバリデーション等に利用できます。

Contains

func Contains(b, subslice []byte) bool

Index 関数のラッパーです。

ContainsAny

func ContainsAny(b []byte, chars string) bool

IndexAny 関数のラッパーです。

ContainsRune

func ContainsRune(b []byte, r rune) bool

IndexRune 関数のラッパーです。

Index

func Index(s, sep []byte) int

検索対象に検索バイトが含まれる場合は、最初の Index を返し、存在しない場合は-1 を返します。

具体的な処理はこのようになります。

検索バイトの長さが 0 の場合 0 を返します。
検索の長さが 1 の場合、アセンブリで実装された IndexByte が呼ばれます。
検索の長さが同じ場合は、Equal 関数を呼び一致するか検証します
検索対象の長さが 32 以下かつ検索バイトスライスの長さがともに 16 以下の場合はアセンブリで実装された `bytelag.Index` 関数を利用します
検索対象の長さが 32 以下かつ検索バイトスライスの長さが 16 より大きい場合は、先頭が一致した箇所があるまで検証し、`bytealg.Index` より高速な`bytealg.IndexByte` 関数を用います。
関数内では`fails`を保持しており、`bytealg.IndexByte` でご検知が多い場合は、`bytealg.Index`を使用します

IndexAny

func IndexAny(s []byte, chars string) int

bytes に chars の文字列のいずれかの文字が含まれるか調べます。存在しない場合は-1 を返し、存在する場合は最初の index を返します。

具体的な処理はこのようになります。

無効な文字は`\uFFFD`に置き換えられるため、この文字が含まれていないか調べます。含まれている場合は 0 を返します。
バイトの長さが 8 より小さい場合は、すべて ASCII に変換しつつ、変換出来ないものがないか検証し、Bytes に一致するものがないか調べます。
それ以外の場合は、`bytealg.IndexByteString``bytealg.IndexString`を利用しつつ愚直に調べていきます。

何故、文字数が少ない場合はアセンブリのコードを呼ばないのか分からなかったのですが、オーバーヘッドなのかなと思いました。こちらについては、後ほど計測しつつ調べていきたいです。

IndexByte

func IndexByte(b []byte, c byte) int

bytealg.IndexByteが呼ばれます。

IndexRune

func IndexRune(s []byte, r rune) int

具体的な処理はこのようになります。

`RuneSelf`(シングルバイト)の場合は`betealg.IndexByte`を利用します
`utf8.RuneError`場合は、文字列から無効なルーンを一つずつ調べ最初に一致するものを返します
無効なルーンの場合は-1 を返します
それ以外の場合は `rune``byte` に変換し Index 関数を呼びます

Count

func Count(s, sep []byte) int

s 内の sep に重複しない数を数えます。sep のスライスが空の場合、1+s の UTF-8 エンコードされた数を返します。

Cut

func Cut(s, sep []byte) (before, after []byte, found bool)

Sep の前後をスライスで返します。

Strings の例ですが、Index 関数で探してスライス二分割する処理を単純にできます 。

Go 1.18: Cut added to strings/bytes - DEV Community 👩‍💻👨‍💻


Equal

Equal

func Equal(a, b []byte) bool

a,b が同じ長さで同じバイトであるかを返します。

EqualFold

func EqualFold(s, t []byte) bool

大文字と小文字を区別せずに同じバイトのスライスであるかを返します。

func Fields(s []byte) [][]byte

スペース で分割します。

Prefix

func HasPrefix(s, prefix []byte) bool

バイトスライスが s で始まるか調べる便利メソッドです。


Join

Join

func Join(s [][]byte, sep []byte) []byte

新しいバイトスライスを作成し、複数のバイトスライス s を連結します。sep は s の要素の間に挿入されます。

Index

LastIndex

func LastIndex(s, sep []byte) int

一致する最後のインスタンスのインデックスを返します。


Repeat/Replace/Split

Repeat

func Repeat(b []byte, count int) []byte

バイトスライス b を count 回繰り返したバイトスライスを返します。

オーバーフローする場合は Panic が発生します。

パフォーマンスのため、チャンクを作り繰り返しのデータをスライスに追加していきます。パフォーマンスの劣化を防ぐため、 チャンクのサイズは最大 8kb になります。

Replace

func Replace(s, old, new []byte, n int) []byte

s にある n 回目の old を new に置き換えます。

ReplaceAll

func ReplaceAll(s, old, new []byte) []byte

s にある old を new に置き換えます。

Runes

func Runes(s []byte) []rune

utf8.DecodeRuneを利用し、バイトスライスと等価な rune を返します。

Split

func Split(s, sep []byte) [][]byte

s を sep で区切りバイトスライスのスライスを返します。


Lower/Upper

ToLower

func ToLower(s []byte) []byte

すべての Unicode を小文字にしたバイトスライスのコピーを返します。

具体的な処理はこのようになります。

ASCII かつ大文字が存在しない場合はバイトスライスにコピーしそのまま返します。
ASCII のみかつシングルバイトの場合は’a’-‘A’分ずらします。
それ以外の場合は`unicode.ToLower`を利用します。

シングルバイトで場合分けをすることで効率的に処理をしています。

ASCII(wikipedia)


Trim

Trim

func Trim(s []byte, cutset string) []byte

s の先頭及び末尾から cutset に含まれる byte をすべて取り除きます。

func main() {
	fmt.Printf("[%q]", bytes.Trim([]byte(" !!! Achtung! Achtung! !!! "), "! "))
}
// ["Achtung! Achtung"]

Buffer

A Buffer is a variable-sized buffer of bytes with Read and Write methods.
The zero value for Buffer is an empty buffer ready to use.

Buffer は Read,Write メソッドを持つ可変サイズのシンプルなバイトバッファです。