Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 19546] ファイルの概要

このコミットは、Go言語の標準ライブラリであるfmtパッケージにおける、16進数フォーマットの挙動変更に関するものです。具体的には、fmt.Printf関数で文字列やバイトスライスを16進数形式(%xまたは%X)で出力する際に、#フラグ(代替形式)を使用した場合の0xプレフィックスの表示方法が改善されました。以前は各バイトの前に0xが付与されていましたが、この変更により、コンパクトな16進数エンコードでは先頭に一度だけ0xが付与されるようになり、可読性が向上しました。

コミット

commit 311e28636ab1b41a10510a46fe1c8728e8713057
Author: Rob Pike <r@golang.org>
Date:   Mon Jun 16 10:45:05 2014 -0700

    fmt: don't put 0x on every byte of a compact hex-encoded string
    Printf("%x", "abc") was "0x610x620x63"; is now "0x616263", which
    is surely better.
    Printf("% #x", "abc") is still "0x61 0x62 0x63".
    
    Fixes #8080.
    
    LGTM=bradfitz, gri
    R=golang-codereviews, bradfitz, gri
    CC=golang-codereviews
    https://golang.org/cl/106990043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/311e28636ab1b41a10510a46fe1c8728e8713057

元コミット内容

fmt: don't put 0x on every byte of a compact hex-encoded string
Printf("%x", "abc") was "0x610x620x63"; is now "0x616263", which
is surely better.
Printf("% #x", "abc") is still "0x61 0x62 0x63".

Fixes #8080.

LGTM=bradfitz, gri
R=golang-codereviews, bradfitz, gri
CC=golang-codereviews
https://golang.org/cl/106990043

変更の背景

この変更の背景には、fmtパッケージのPrintf関数における16進数フォーマット(%x%X)の挙動に関するユーザビリティと可読性の問題がありました。特に、#フラグ(代替形式)を使用して0xプレフィックスを付与する場合、文字列やバイトスライスを16進数エンコードすると、各バイトの前に冗長に0xが繰り返し付与されていました。

例えば、文字列 "abc"%#xでフォーマットすると、以前は "0x610x620x63" のように出力されていました。これは、各バイト(a0x61b0x62c0x63)ごとに0xプレフィックスが付与されるためです。しかし、このような出力は、バイト列全体を一つの16進数表現として見たい場合には非常に読みにくく、期待される「コンパクトな16進数エンコード」とはかけ離れていました。

このコミットは、この冗長な0xプレフィックスの挙動を修正し、より直感的で読みやすい出力形式を提供することを目的としています。具体的には、%#x%#Xでバイトスライスや文字列をフォーマットする際に、0xプレフィックスは全体の先頭に一度だけ付与されるように変更されました。これにより、"abc""0x616263"と出力されるようになり、よりコンパクトで一般的な16進数表現に近づきました。

ただし、% #xのようにスペースフラグも同時に使用する場合は、以前と同様に各バイトの前に0xが付き、かつスペースで区切られる挙動が維持されます。これは、各バイトを個別の16進数として明確に区別したい場合に有用な形式であるため、その挙動は変更されませんでした。

この変更は、Goのfmtパッケージの設計思想である「実用性と明確性」に沿ったものであり、開発者がより自然な形で16進数表現を扱えるようにするための改善と言えます。

前提知識の解説

このコミットを理解するためには、Go言語のfmtパッケージと、特にPrintf関数におけるフォーマット動詞(verb)とフラグに関する基本的な知識が必要です。

  1. fmtパッケージ: Go言語の標準ライブラリの一つで、フォーマットされたI/O(入出力)を提供します。C言語のprintf/scanfに似た機能を提供し、様々なデータ型を整形して文字列として出力したり、文字列からデータを読み取ったりすることができます。

  2. fmt.Printf関数: 指定されたフォーマット文字列に従って値を整形し、標準出力に出力する関数です。 func Printf(format string, a ...interface{}) (n int, err error)

  3. フォーマット動詞(Verbs): フォーマット文字列内で使用される特殊な文字の組み合わせで、引数の値をどのように整形するかを指定します。

    • %x: 符号なし整数を小文字の16進数で表現します。バイトスライスや文字列に対して使用すると、そのバイト列を16進数でエンコードします。
    • %X: 符号なし整数を大文字の16進数で表現します。バイトスライスや文字列に対して使用すると、そのバイト列を大文字の16進数でエンコードします。
  4. フラグ(Flags): フォーマット動詞の前に置かれるオプションの文字で、出力の挙動をさらに制御します。

    • # (シャープフラグ):
      • 数値型の場合(%o, %x, %X):基数プレフィックス(00x0X)を付与します。
      • 文字列型の場合(%q):引用符で囲みます。
      • このコミットの文脈では、%x%Xと共に使用された場合に0xまたは0Xプレフィックスを付与する役割が重要です。
    • (スペースフラグ):
      • 数値型の場合:符号なしの数値の前にスペースを挿入します。
      • このコミットの文脈では、% xのように%xと共に使用された場合に、各バイトの16進数表現をスペースで区切る役割が重要です。
  5. バイトスライスと文字列の16進数エンコード: Goでは、文字列はUTF-8エンコードされたバイトスライスとして内部的に扱われます。%x%Xを文字列やバイトスライスに適用すると、そのバイト列が16進数表現に変換されます。例えば、ASCII文字の'a'は16進数で0x61'b'0x62'c'0x63です。

このコミットは、特に#フラグと フラグの組み合わせ、およびバイトスライス/文字列の16進数エンコードにおける0xプレフィックスの表示ロジックの変更に焦点を当てています。

技術的詳細

このコミットの技術的な核心は、fmtパッケージ内の16進数フォーマット処理ロジック、特にformat.goファイル内のfmt_sbx関数における条件式の変更にあります。

fmt_sbx関数は、文字列(s)またはバイトスライス(b)を16進数形式でフォーマットする役割を担っています。この関数は、入力されたバイト列を1バイトずつ処理し、それぞれのバイトを2桁の16進数に変換して出力バッファに追加します。

変更前のコードでは、#フラグ(f.sharpがtrue)が設定されている場合、各バイトの16進数表現の前に無条件に0x(または0X)プレフィックスが付与されていました。

// 変更前 (format.go)
if f.sharp { // f.sharpがtrueの場合、常に0xを付与
    buf = append(buf, '0', x) // xは'x'または'X'
}

このロジックにより、例えば[]byte("abc")%#xでフォーマットすると、0x610x620x63のように、各バイトの前に0xが繰り返し付与されていました。これは、コンパクトな16進数表現としては冗長であり、可読性を損ねていました。

このコミットでは、この条件式が以下のように変更されました。

// 変更後 (format.go)
if f.sharp && (f.space || i == 0) { // f.sharpがtrue かつ (f.spaceがtrue または 最初のバイト (i==0)) の場合のみ0xを付与
    buf = append(buf, '0', x)
}

この変更のポイントは、f.sharp#フラグ)が設定されている場合に加えて、以下のいずれかの条件が満たされる場合にのみ0xプレフィックスを付与するようにした点です。

  1. f.spaceがtrueの場合: これは、 (スペース)フラグが設定されていることを意味します。例えば、% #xのように指定された場合です。この場合、各バイトの16進数表現がスペースで区切られるため、各バイトの前に0xを付与しても冗長性が低く、個々のバイトの識別を助けるため、以前の挙動が維持されます(例: 0x61 0x62 0x63)。
  2. i == 0の場合: これは、現在処理しているバイトがバイト列の最初のバイトであることを意味します。この条件により、#フラグが設定されていて、かつスペースフラグが設定されていない場合(例: %#x)、0xプレフィックスはバイト列全体の先頭に一度だけ付与されるようになります。これにより、0x616263のようなコンパクトな表現が実現されます。

この論理変更により、#フラグの挙動がより直感的になり、コンパクトな16進数表現では先頭に一度だけプレフィックスが付与され、各バイトを区切りたい場合にはスペースフラグと組み合わせて使用することで、以前の挙動を維持できるようになりました。

この変更は、fmtパッケージのテストファイルであるfmt_test.goにも反映され、新しい挙動を検証するためのテストケースが追加・修正されています。これにより、意図した通りにフォーマットが行われることが保証されます。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更箇所は以下の2つのファイルです。

  1. src/pkg/fmt/fmt_test.go:

    • このファイルはfmtパッケージのテストコードを含んでいます。
    • 変更点: fmtTestsというテストケースのスライスに、新しい%#xおよび%#Xフォーマットの期待される出力が追加・修正されています。特に、バイトスライスに対する%#x%#Xのテストケースが、以前の0x610x620x63ffのような冗長な形式から、0x616263ffのようなコンパクトな形式に修正されています。また、%# x(スペースフラグも含む)のテストケースは以前の挙動を維持していることを確認しています。
    --- a/src/pkg/fmt/fmt_test.go
    +++ b/src/pkg/fmt/fmt_test.go
    @@ -125,13 +125,17 @@ var fmtTests = []struct {
     	{"%x", "xyz", "78797a"},
     	{"%X", "xyz", "78797A"},
     	{"%q", "abc", `\"abc\"`},
    +\t{\"%#x\", []byte(\"abc\\xff\"), \"0x616263ff\"},
    +\t{\"%#X\", []byte(\"abc\\xff\"), \"0X616263FF\"},
    +\t{\"%# x\", []byte(\"abc\\xff\"), \"0x61 0x62 0x63 0xff\"},
    +\t{\"%# X\", []byte(\"abc\\xff\"), \"0X61 0X62 0X63 0XFF\"},
     
     	// basic bytes
     	{\"%s\", []byte(\"abc\"), \"abc\"},
     	{\"%x\", []byte(\"abc\"), \"616263\"},
     	{\"% x\", []byte(\"abc\\xff\"), \"61 62 63 ff\"},
    -\t{\"%#x\", []byte(\"abc\\xff\"), \"0x610x620x630xff\"},
    -\t{\"%#X\", []byte(\"abc\\xff\"), \"0X610X620X630XFF\"},
    +\t{\"%#x\", []byte(\"abc\\xff\"), \"0x616263ff\"},
    +\t{\"%#X\", []byte(\"abc\\xff\"), \"0X616263FF\"},
     	{\"%# x\", []byte(\"abc\\xff\"), \"0x61 0x62 0x63 0xff\"},
     	{\"%# X\", []byte(\"abc\\xff\"), \"0X61 0X62 0X63 0XFF\"},
     	{\"% X\", []byte(\"abc\\xff\"), \"61 62 63 FF\"},
    @@ -379,7 +383,7 @@ var fmtTests = []struct {
     	{\"%s\", I(23), `<23>`},
     	{\"%q\", I(23), `\"<23>\"`},
     	{\"%x\", I(23), `3c32333e`},
    -\t{\"%#x\", I(23), `0x3c0x320x330x3e`},\n+\t{\"%#x\", I(23), `0x3c32333e`},
     	{\"%# x\", I(23), `0x3c 0x32 0x33 0x3e`},
     	{\"%d\", I(23), `23`}, // Stringer applies only to string formats.
    
  2. src/pkg/fmt/format.go:

    • このファイルはfmtパッケージの実際のフォーマットロジックを含んでいます。
    • 変更点: fmt構造体のfmt_sbxメソッド内の条件式が1行変更されています。この変更が、0xプレフィックスの付与ロジックの核心です。
    --- a/src/pkg/fmt/format.go
    +++ b/src/pkg/fmt/format.go
    @@ -298,7 +298,7 @@ func (f *fmt) fmt_sbx(s string, b []byte, digits string) {
     		if i > 0 && f.space {
     			buf = append(buf, ' ')
     		}
    -\t\tif f.sharp {\n+\t\tif f.sharp && (f.space || i == 0) {
     			buf = append(buf, '0', x)
     		}
     		var c byte
    

コアとなるコードの解説

src/pkg/fmt/fmt_test.go の変更

このファイルの変更は、主に新しいフォーマット挙動を検証するためのテストケースの更新です。 fmtTestsスライスは、様々なフォーマット文字列と入力値、そしてそれらに対する期待される出力のペアを定義しています。

  • 変更前: {"%#x", []byte("abc\\xff"), "0x610x620x630xff"} {"%#X", []byte("abc\\xff"), "0X610X620X630XFF"} これらのテストケースは、#フラグが設定されている場合に、各バイトの前に0x(または0X)が繰り返し付与されるという、変更前の挙動を反映していました。

  • 変更後: {"%#x", []byte("abc\\xff"), "0x616263ff"} {"%#X", []byte("abc\\xff"), "0X616263FF"} これらのテストケースは、#フラグが設定されていても、コンパクトな16進数エンコードでは先頭に一度だけ0x(または0X)が付与されるという、新しい挙動を反映するように修正されました。

  • 追加されたテストケース: {"%#x", []byte("abc\\xff"), "0x616263ff"} {"%#X", []byte("abc\\xff"), "0X616263FF"} これらは、文字列リテラルに対する%#xおよび%#Xの新しい挙動をテストするために追加されました。

  • 維持されたテストケース: {"%# x", []byte("abc\\xff"), "0x61 0x62 0x63 0xff"} {"%# X", []byte("abc\\xff"), "0X61 0X62 0X63 0XFF"} これらのテストケースは、#フラグとスペースフラグの両方が設定されている場合(% #x)には、以前と同様に各バイトの前に0xが付き、スペースで区切られる挙動が維持されることを確認しています。これは、この特定のフォーマットが各バイトを個別に識別する目的で使われるため、その有用性を保つための意図的な設計です。

これらのテストケースの変更は、format.goで行われたロジック変更が正しく機能し、期待される出力が得られることを保証するためのものです。

src/pkg/fmt/format.go の変更

このファイルにおける変更は、fmt構造体のfmt_sbxメソッド内のたった1行の条件式の変更ですが、これがこのコミットの機能的な核心です。

fmt_sbxメソッドは、バイトスライスや文字列を16進数形式に変換する際の主要なロジックを含んでいます。このメソッドは、入力されたバイト列をループで1バイトずつ処理します。

  • 変更前:

    if f.sharp {
        buf = append(buf, '0', x)
    }
    

    この条件式は非常に単純で、#フラグ(f.sharp)が設定されていれば、現在処理しているバイトの16進数表現の前に無条件に0x(または0X)プレフィックスをbuf(出力バッファ)に追加していました。このため、各バイトごとに0xが繰り返し付与される結果となっていました。

  • 変更後:

    if f.sharp && (f.space || i == 0) {
        buf = append(buf, '0', x)
    }
    

    この変更により、0xプレフィックスが付与される条件がより厳密になりました。

    • f.sharp: これは引き続き必須条件です。#フラグがなければ0xは付与されません。
    • (f.space || i == 0): この部分が追加された新しい条件です。
      • f.space: スペースフラグが設定されている場合(例: % #x)。この場合、各バイトがスペースで区切られるため、各バイトの前に0xを付与しても冗長性が低く、個々のバイトの識別を助けるため、0xが引き続き付与されます。
      • i == 0: 現在処理しているバイトがバイト列の最初のバイトである場合。これは、ループ変数i0、つまりバイト列の先頭の要素であることを意味します。この条件により、#フラグが設定されていて、かつスペースフラグが設定されていない場合(例: %#x)、0xプレフィックスはバイト列全体の先頭に一度だけ付与されるようになります。

この論理変更により、fmt.Printf%#xおよび%#Xフォーマットは、より一般的な16進数表現の慣習に沿った、コンパクトで読みやすい出力を提供するようになりました。

関連リンク

参考にした情報源リンク

[インデックス 19546] ファイルの概要

このコミットは、Go言語の標準ライブラリであるfmtパッケージにおける、16進数フォーマットの挙動変更に関するものです。具体的には、fmt.Printf関数で文字列やバイトスライスを16進数形式(%xまたは%X)で出力する際に、#フラグ(代替形式)を使用した場合の0xプレフィックスの表示方法が改善されました。以前は各バイトの前に0xが付与されていましたが、この変更により、コンパクトな16進数エンコードでは先頭に一度だけ0xが付与されるようになり、可読性が向上しました。

コミット

commit 311e28636ab1b41a10510a46fe1c8728e8713057
Author: Rob Pike <r@golang.org>
Date:   Mon Jun 16 10:45:05 2014 -0700

    fmt: don't put 0x on every byte of a compact hex-encoded string
    Printf("%x", "abc") was "0x610x620x63"; is now "0x616263", which
    is surely better.
    Printf("% #x", "abc") is still "0x61 0x62 0x63".
    
    Fixes #8080.
    
    LGTM=bradfitz, gri
    R=golang-codereviews, bradfitz, gri
    CC=golang-codereviews
    https://golang.org/cl/106990043

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/311e28636ab1b41a10510a46fe1c8728e8713057

元コミット内容

fmt: don't put 0x on every byte of a compact hex-encoded string
Printf("%x", "abc") was "0x610x620x63"; is now "0x616263", which
is surely better.
Printf("% #x", "abc") is still "0x61 0x62 0x63".

Fixes #8080.

LGTM=bradfitz, gri
R=golang-codereviews, bradfitz, gri
CC=golang-codereviews
https://golang.org/cl/106990043

変更の背景

この変更の背景には、fmtパッケージのPrintf関数における16進数フォーマット(%x%X)の挙動に関するユーザビリティと可読性の問題がありました。特に、#フラグ(代替形式)を使用して0xプレフィックスを付与する場合、文字列やバイトスライスを16進数エンコードすると、各バイトの前に冗長に0xが繰り返し付与されていました。

例えば、文字列 "abc"%#xでフォーマットすると、以前は "0x610x620x63" のように出力されていました。これは、各バイト(a0x61b0x62c0x63)ごとに0xプレフィックスが付与されるためです。しかし、このような出力は、バイト列全体を一つの16進数表現として見たい場合には非常に読みにくく、期待される「コンパクトな16進数エンコード」とはかけ離れていました。

このコミットは、この冗長な0xプレフィックスの挙動を修正し、より直感的で読みやすい出力形式を提供することを目的としています。具体的には、%#x%#Xでバイトスライスや文字列をフォーマットする際に、0xプレフィックスは全体の先頭に一度だけ付与されるように変更されました。これにより、"abc""0x616263"と出力されるようになり、よりコンパクトで一般的な16進数表現に近づきました。

ただし、% #xのようにスペースフラグも同時に使用する場合は、以前と同様に各バイトの前に0xが付き、かつスペースで区切られる挙動が維持されます。これは、各バイトを個別の16進数として明確に区別したい場合に有用な形式であるため、その挙動は変更されませんでした。

この変更は、Goのfmtパッケージの設計思想である「実用性と明確性」に沿ったものであり、開発者がより自然な形で16進数表現を扱えるようにするための改善と言えます。

前提知識の解説

このコミットを理解するためには、Go言語のfmtパッケージと、特にPrintf関数におけるフォーマット動詞(verb)とフラグに関する基本的な知識が必要です。

  1. fmtパッケージ: Go言語の標準ライブラリの一つで、フォーマットされたI/O(入出力)を提供します。C言語のprintf/scanfに似た機能を提供し、様々なデータ型を整形して文字列として出力したり、文字列からデータを読み取ったりすることができます。

  2. fmt.Printf関数: 指定されたフォーマット文字列に従って値を整形し、標準出力に出力する関数です。 func Printf(format string, a ...interface{}) (n int, err error)

  3. フォーマット動詞(Verbs): フォーマット文字列内で使用される特殊な文字の組み合わせで、引数の値をどのように整形するかを指定します。

    • %x: 符号なし整数を小文字の16進数で表現します。バイトスライスや文字列に対して使用すると、そのバイト列を16進数でエンコードします。
    • %X: 符号なし整数を大文字の16進数で表現します。バイトスライスや文字列に対して使用すると、そのバイト列を大文字の16進数でエンコードします。
  4. フラグ(Flags): フォーマット動詞の前に置かれるオプションの文字で、出力の挙動をさらに制御します。

    • # (シャープフラグ):
      • 数値型の場合(%o, %x, %X):基数プレフィックス(00x0X)を付与します。
      • 文字列型の場合(%q):引用符で囲みます。
      • このコミットの文脈では、%x%Xと共に使用された場合に0xまたは0Xプレフィックスを付与する役割が重要です。
    • (スペースフラグ):
      • 数値型の場合:符号なしの数値の前にスペースを挿入します。
      • このコミットの文脈では、% xのように%xと共に使用された場合に、各バイトの16進数表現をスペースで区切る役割が重要です。
  5. バイトスライスと文字列の16進数エンコード: Goでは、文字列はUTF-8エンコードされたバイトスライスとして内部的に扱われます。%x%Xを文字列やバイトスライスに適用すると、そのバイト列が16進数表現に変換されます。例えば、ASCII文字の'a'は16進数で0x61'b'0x62'c'0x63です。

このコミットは、特に#フラグと フラグの組み合わせ、およびバイトスライス/文字列の16進数エンコードにおける0xプレフィックスの表示ロジックの変更に焦点を当てています。

技術的詳細

このコミットの技術的な核心は、fmtパッケージ内の16進数フォーマット処理ロジック、特にformat.goファイル内のfmt_sbx関数における条件式の変更にあります。

fmt_sbx関数は、文字列(s)またはバイトスライス(b)を16進数形式でフォーマットする役割を担っています。この関数は、入力されたバイト列を1バイトずつ処理し、それぞれのバイトを2桁の16進数に変換して出力バッファに追加します。

変更前のコードでは、#フラグ(f.sharpがtrue)が設定されている場合、各バイトの16進数表現の前に無条件に0x(または0X)プレフィックスが付与されていました。

// 変更前 (format.go)
if f.sharp { // f.sharpがtrueの場合、常に0xを付与
    buf = append(buf, '0', x) // xは'x'または'X'
}

このロジックにより、例えば[]byte("abc")%#xでフォーマットすると、0x610x620x63のように、各バイトの前に0xが繰り返し付与されていました。これは、コンパクトな16進数表現としては冗長であり、可読性を損ねていました。

このコミットでは、この条件式が以下のように変更されました。

// 変更後 (format.go)
if f.sharp && (f.space || i == 0) { // f.sharpがtrue かつ (f.spaceがtrue または 最初のバイト (i==0)) の場合のみ0xを付与
    buf = append(buf, '0', x)
}

この変更のポイントは、f.sharp#フラグ)が設定されている場合に加えて、以下のいずれかの条件が満たされる場合にのみ0xプレフィックスを付与するようにした点です。

  1. f.spaceがtrueの場合: これは、 (スペース)フラグが設定されていることを意味します。例えば、% #xのように指定された場合です。この場合、各バイトの16進数表現がスペースで区切られるため、各バイトの前に0xを付与しても冗長性が低く、個々のバイトの識別を助けるため、以前の挙動が維持されます(例: 0x61 0x62 0x63)。
  2. i == 0の場合: これは、現在処理しているバイトがバイト列の最初のバイトであることを意味します。この条件により、#フラグが設定されていて、かつスペースフラグが設定されていない場合(例: %#x)、0xプレフィックスはバイト列全体の先頭に一度だけ付与されるようになります。これにより、0x616263のようなコンパクトな表現が実現されます。

この論理変更により、#フラグの挙動がより直感的になり、コンパクトな16進数表現では先頭に一度だけプレフィックスが付与され、各バイトを区切りたい場合にはスペースフラグと組み合わせて使用することで、以前の挙動を維持できるようになりました。

この変更は、fmtパッケージのテストファイルであるfmt_test.goにも反映され、新しい挙動を検証するためのテストケースが追加・修正されています。これにより、意図した通りにフォーマットが行われることが保証されます。

コアとなるコードの変更箇所

このコミットにおけるコアとなるコードの変更箇所は以下の2つのファイルです。

  1. src/pkg/fmt/fmt_test.go:

    • このファイルはfmtパッケージのテストコードを含んでいます。
    • 変更点: fmtTestsというテストケースのスライスに、新しい%#xおよび%#Xフォーマットの期待される出力が追加・修正されています。特に、バイトスライスに対する%#x%#Xのテストケースが、以前の0x610x620x63ffのような冗長な形式から、0x616263ffのようなコンパクトな形式に修正されています。また、%# x(スペースフラグも含む)のテストケースは以前の挙動を維持していることを確認しています。
    --- a/src/pkg/fmt/fmt_test.go
    +++ b/src/pkg/fmt/fmt_test.go
    @@ -125,13 +125,17 @@ var fmtTests = []struct {
     	{"%x", "xyz", "78797a"},
     	{"%X", "xyz", "78797A"},
     	{"%q", "abc", `\"abc\"`},
    +\t{\"%#x\", []byte(\"abc\\xff\"), \"0x616263ff\"},
    +\t{\"%#X\", []byte(\"abc\\xff\"), \"0X616263FF\"},
    +\t{\"%# x\", []byte(\"abc\\xff\"), \"0x61 0x62 0x63 0xff\"},
    +\t{\"%# X\", []byte(\"abc\\xff\"), \"0X61 0X62 0X63 0XFF\"},
     
     	// basic bytes
     	{\"%s\", []byte(\"abc\"), \"abc\"},
     	{\"%x\", []byte(\"abc\"), \"616263\"},
     	{\"% x\", []byte(\"abc\\xff\"), \"61 62 63 ff\"},
    -\t{\"%#x\", []byte(\"abc\\xff\"), \"0x610x620x630xff\"},
    -\t{\"%#X\", []byte(\"abc\\xff\"), \"0X610X620X630XFF\"},
    +\t{\"%#x\", []byte(\"abc\\xff\"), \"0x616263ff\"},
    +\t{\"%#X\", []byte(\"abc\\xff\"), \"0X616263FF\"},
     	{\"%# x\", []byte(\"abc\\xff\"), \"0x61 0x62 0x63 0xff\"},
     	{\"%# X\", []byte(\"abc\\xff\"), \"0X61 0X62 0X63 0XFF\"},
     	{\"% X\", []byte(\"abc\\xff\"), \"61 62 63 FF\"},
    @@ -379,7 +383,7 @@ var fmtTests = []struct {
     	{\"%s\", I(23), `<23>`},
     	{\"%q\", I(23), `\"<23>\"`},
     	{\"%x\", I(23), `3c32333e`},
    -\t{\"%#x\", I(23), `0x3c0x320x330x3e`},\n+\t{\"%#x\", I(23), `0x3c32333e`},
     	{\"%# x\", I(23), `0x3c 0x32 0x33 0x3e`},
     	{\"%d\", I(23), `23`}, // Stringer applies only to string formats.
    
  2. src/pkg/fmt/format.go:

    • このファイルはfmtパッケージの実際のフォーマットロジックを含んでいます。
    • 変更点: fmt構造体のfmt_sbxメソッド内の条件式が1行変更されています。この変更が、0xプレフィックスの付与ロジックの核心です。
    --- a/src/pkg/fmt/format.go
    +++ b/src/pkg/fmt/format.go
    @@ -298,7 +298,7 @@ func (f *fmt) fmt_sbx(s string, b []byte, digits string) {
     		if i > 0 && f.space {
     			buf = append(buf, ' ')
     		}
    -\t\tif f.sharp {\n+\t\tif f.sharp && (f.space || i == 0) {
     			buf = append(buf, '0', x)
     		}
     		var c byte
    

コアとなるコードの解説

src/pkg/fmt/fmt_test.go の変更

このファイルの変更は、主に新しいフォーマット挙動を検証するためのテストケースの更新です。 fmtTestsスライスは、様々なフォーマット文字列と入力値、そしてそれらに対する期待される出力のペアを定義しています。

  • 変更前: {"%#x", []byte("abc\\xff"), "0x610x620x630xff"} {"%#X", []byte("abc\\xff"), "0X610X620X630XFF"} これらのテストケースは、#フラグが設定されている場合に、各バイトの前に0x(または0X)が繰り返し付与されるという、変更前の挙動を反映していました。

  • 変更後: {"%#x", []byte("abc\\xff"), "0x616263ff"} {"%#X", []byte("abc\\xff"), "0X616263FF"} これらのテストケースは、#フラグが設定されていても、コンパクトな16進数エンコードでは先頭に一度だけ0x(または0X)が付与されるという、新しい挙動を反映するように修正されました。

  • 追加されたテストケース: {"%#x", []byte("abc\\xff"), "0x616263ff"} {"%#X", []byte("abc\\xff"), "0X616263FF"} これらは、文字列リテラルに対する%#xおよび%#Xの新しい挙動をテストするために追加されました。

  • 維持されたテストケース: {"%# x", []byte("abc\\xff"), "0x61 0x62 0x63 0xff"} {"%# X", []byte("abc\\xff"), "0X61 0X62 0X63 0XFF"} これらのテストケースは、#フラグとスペースフラグの両方が設定されている場合(% #x)には、以前と同様に各バイトの前に0xが付き、スペースで区切られる挙動が維持されることを確認しています。これは、この特定のフォーマットが各バイトを個別に識別する目的で使われるため、その有用性を保つための意図的な設計です。

これらのテストケースの変更は、format.goで行われたロジック変更が正しく機能し、期待される出力が得られることを保証するためのものです。

src/pkg/fmt/format.go の変更

このファイルにおける変更は、fmt構造体のfmt_sbxメソッド内のたった1行の条件式の変更ですが、これがこのコミットの機能的な核心です。

fmt_sbxメソッドは、バイトスライスや文字列を16進数形式に変換する際の主要なロジックを含んでいます。このメソッドは、入力されたバイト列をループで1バイトずつ処理します。

  • 変更前:

    if f.sharp {
        buf = append(buf, '0', x)
    }
    

    この条件式は非常に単純で、#フラグ(f.sharp)が設定されていれば、現在処理しているバイトの16進数表現の前に無条件に0x(または0X)プレフィックスをbuf(出力バッファ)に追加していました。このため、各バイトごとに0xが繰り返し付与される結果となっていました。

  • 変更後:

    if f.sharp && (f.space || i == 0) {
        buf = append(buf, '0', x)
    }
    

    この変更により、0xプレフィックスが付与される条件がより厳密になりました。

    • f.sharp: これは引き続き必須条件です。#フラグがなければ0xは付与されません。
    • (f.space || i == 0): この部分が追加された新しい条件です。
      • f.space: スペースフラグが設定されている場合(例: % #x)。この場合、各バイトがスペースで区切られるため、各バイトの前に0xを付与しても冗長性が低く、個々のバイトの識別を助けるため、0xが引き続き付与されます。
      • i == 0: 現在処理しているバイトがバイト列の最初のバイトである場合。これは、ループ変数i0、つまりバイト列の先頭の要素であることを意味します。この条件により、#フラグが設定されていて、かつスペースフラグが設定されていない場合(例: %#x)、0xプレフィックスはバイト列全体の先頭に一度だけ付与されるようになります。

この論理変更により、fmt.Printf%#xおよび%#Xフォーマットは、より一般的な16進数表現の慣習に沿った、コンパクトで読みやすい出力を提供するようになりました。

関連リンク

参考にした情報源リンク