ホームページ  >  記事  >  バックエンド開発  >  Go 文字列解析

Go 文字列解析

Guanhui
Guanhui転載
2020-06-12 18:21:233062ブラウズ

Go 文字列解析

#文字列とは何ですか?

Go では、文字列は (空の場合もある) 不変のバイトのシーケンスです。私たちにとって、ここでのキーワードは不変です。バイトスライスは可変であるため、文字列と []byte の間の変換には通常、割り当てとコピーが必要であり、コストがかかります。

内部では、Go の文字列は (現時点では) 長さと文字列データへのポインタとして表されます。

文字列常駐とは何ですか?

次のコードについて考えてみましょう:

b := []byte("hello")
s := string(b)
t := string(b)

s と t は文字列なので、どちらも長さとデータ ポインターを持っています。それらの長さは明らかに同じです。データ ポインタはどうなるのでしょうか?

Go 言語では、直接の検索方法を提供できません。ただし、unsafe を使用してプローブすることはできます:

func pointer(s string) uintptr {
    p := unsafe.Pointer(&s)
    h := *(*reflect.StringHeader)(p)
    return h.Data
}

(この関数は unsafe.Pointer を返す必要があります。詳細については Go 問題 19367 を参照してください。)

If we fmt.Println(pointer(s), pointer ( t))、4302664 4302632 のような情報が得られます。ポインターは異なります。ポインターにはデータの 2 つの別々のコピーがあります。

(これは演習リンクです。試してみたい場合は、「hello」を「h」に変更するとどうなりますか? 説明)

単一データ hello を再利用するとします。コピー?これが文字列常駐です。文字列常駐には 2 つの利点があります。明らかな利点は、データの割り当てやコピーが必要ないことです。もう 1 つの利点は、文字列の等価性チェックが高速化されることです。 2 つの文字列が同じ長さと同じデータ ポインタを持っている場合、それらは等しいため、バイトをチェックする必要はありません。

Go 1.14 の時点では、Go はほとんどの文字列を永続化しません。他の形式のキャッシュと同様、永続化には同時実行の安全性のための同期、ガベージ コレクターの複雑さ、文字列が作成されるたびに実行される追加のコードなどのコストがかかります。また、キャッシュと同様に、役立つというよりも有害になる可能性がある状況もあります。辞書内の単語を扱う場合、単語が 2 回出現することはありません。また、文字列の永続化は時間とメモリの無駄です。

手動による文字列の永続化

Go では文字列を手動で永続化できます。必要なのは、おそらく map[[]byte]string のようなものを使用して、バイト スライスを指定して再利用する既存の文字列を見つける方法です。ルックアップが成功した場合は既存の文字列が使用され、失敗した場合は将来の使用に備えて文字列を変換して保存します。

ここには問題が 1 つだけあります。[]byte をマップのキーとして使用することはできません。

長期にわたるコンパイラの最適化のおかげで、代わりに map[string]string を使用できるようになりました。ここでの最適化は、キーが変換されたバイトのスライスであるマップ操作が、検索中に使用される新しい文字列を実際に生成しないことです。

m := make(map[string]string)
b := []byte("hello")
s := string(b) // 分配了
_ = m[string(b)] // 不分配!

(同様の最適化は、変換されたバイト スライスが使用中に変更されないことをコンパイラが証明できる他のケースにも適用されます。たとえば、副作用がない場合にすべてが切り替わるスイッチ string(b) などです。)

文字列を永続化するために必要なコードは次のとおりです:

func intern(m map[string]string, b []byte) string {
    // 查找一个存在的字符串来重用
    c, ok := m[string(b)]
    if ok {
        // 找到一个存在的字符串
        return c
    }
    // 没有找到,所以制作一个并且存储它
    s := string(b)
    m[s] = s
    return s
}

簡単です

新たな問題 (同時実行の症状)

この手動の滞留ルーチンは滞留の問題を呼び出しコードに押し込むことに注意してください。マップへの同時アクセスを管理する必要があり、マップ (およびその中のすべて) の有効期間を決定する必要があり、文字列が必要になるたびにマップ検索の追加コストを支払う必要があります。

これらの決定を呼び出しコードにプッシュすると、パフォーマンスが向上します。たとえば、json を map[string]interface{} にデコードするとします。 json デコーダは同時実行できない可能性があります。マップのライフサイクルは json デコーダーに関連付けることができます。また、このマップのキーは頻繁に繰り返される可能性が高く、これが文字列常駐にとって最良のケースであるため、追加のマップ検索コストにはそれだけの価値があります。

ヘルパー パッケージ

これらの複雑な問題を考慮したくないが、多少のパフォーマンスの低下を許容し、そこに文字列を常駐させる場合は、役立つコードについては、github.com/josharian/intern にパッケージがあります。

その仕組みは、sync.Pool をひどく悪用しています。常駐マップを sync.Pool に保存し、必要に応じて取得します。 sync.Pool への同時アクセスは安全であるため、これにより同時アクセスの問題が非常にうまく解決されます。 sync.Pool 内のコンテンツは通常、最終的にガベージ コレクションされるため、主に有効期間の問題が解決されます。 (ライフタイムの管理に関する関連資料については、Go の問題 29696 を参照してください。)

推奨チュートリアル: "

PHP" "GO Tutorial"

以上がGo 文字列解析の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はlearnku.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。