[インデックス 10149] ファイルの概要
このコミットは、Go言語の実験的なexp/norm
パッケージにおける正規表現のバグ修正に関するものです。具体的には、新しい正規表現エンジンへの移行に伴って発生した、テストブロックの最終行が無視されるという問題に対処しています。
コミット
commit eef78091932aafec2b8030bc1927699b551d119b
Author: Marcel van Lohuizen <mpvl@golang.org>
Date: Mon Oct 31 10:58:04 2011 +0100
exp/norm: fixed bug that creeped in with moving to the new
regexp, which caused the last line of a test block to be ignored.
R=r, rsc
CC=golang-dev
https://golang.org/cl/5177052
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/eef78091932aafec2b8030bc1927699b551d119b
元コミット内容
exp/norm
: 新しい正規表現への移行に伴って忍び込んだバグを修正。これにより、テストブロックの最終行が無視される問題が発生していた。
変更の背景
このコミットは、Go言語のexp/norm
パッケージにおいて発生した特定のバグを修正するために行われました。コミットメッセージによると、このバグは「新しい正規表現への移行に伴って忍び込んだ」ものであり、「テストブロックの最終行が無視される」という問題を引き起こしていました。
exp/norm
パッケージは、Unicodeの正規化(Normalization)に関連する機能を提供していた実験的なパッケージです。Unicode正規化とは、異なるバイト列で表現されうるが、意味的には同じである文字シーケンス(例: アクセント付き文字が単一のコードポイントで表現される場合と、基本文字と結合文字の組み合わせで表現される場合)を、一貫した形式に変換するプロセスです。これにより、文字列の比較や検索が正しく行われるようになります。
このバグは、exp/norm
パッケージ内のテストコードが、特定の正規表現パターンを用いてテストデータを解析する際に発生しました。新しい正規表現エンジンへの変更が、既存の正規表現の挙動に微妙な影響を与え、結果としてテストデータの最終行が正しく処理されなくなっていたと考えられます。これは、テストの網羅性や正確性に直接影響するため、重要な修正でした。
前提知識の解説
1. Go言語のexp/norm
パッケージ
exp/norm
は、Go言語の標準ライブラリの実験的なセクションに存在したパッケージです。その名の通り、Unicodeの正規化(Normalization)に関する機能を提供していました。Unicode正規化は、異なる表現を持つ同じ文字シーケンスを標準的な形式に変換するプロセスであり、文字列の比較やソートにおいて非常に重要です。例えば、「é」という文字は、単一のコードポイント(U+00E9)で表現することもできますし、「e」(U+0065)と結合アキュートアクセント(U+0301)の組み合わせで表現することもできます。正規化は、これらを一貫した形式に変換します。
現在のGo言語では、Unicode正規化は主にgolang.org/x/text/unicode/norm
パッケージで提供されています。exp/norm
は古い実験的なパッケージであり、現在は非推奨またはメンテナンスされていない可能性が高いです。
2. Go言語のregexp
パッケージ
Go言語のregexp
パッケージは、正規表現を扱うための標準ライブラリです。PerlやPythonに似た一般的な正規表現構文(RE2構文)をサポートしており、非常に高速で、入力サイズに対して線形時間で動作することが保証されています。
regexp.MustCompile(expression string)
: この関数は、与えられた正規表現文字列をコンパイルして*Regexp
オブジェクトを返します。コンパイルに失敗した場合(正規表現の構文が不正な場合)はパニック(プログラムの異常終了)を引き起こします。これは、正規表現がコンパイル時に既知であり、常に有効であることが期待される場合によく使用されます。
3. 正規表現の基本とアンカー
正規表現(Regular Expression)は、文字列のパターンを記述するための強力なツールです。このコミットで問題となったのは、正規表現における「アンカー」の概念です。
-
^
(キャレット): 正規表現において、^
は「行の先頭」にマッチするアンカーです。例えば、^abc
という正規表現は、「abc」という文字列が行の先頭にある場合にのみマッチします。行の途中にある「abc」にはマッチしません。 -
.
(ドット):.
は「任意の一文字」(改行を除く)にマッチするメタ文字です。 -
*
(アスタリスク):*
は直前の要素が0回以上繰り返されることにマッチします。 -
+
(プラス):+
は直前の要素が1回以上繰り返されることにマッチします。 -
[0-9A-F\\.]
: これは文字クラスであり、0
から9
までの数字、A
からF
までの大文字、またはリテラルの.
(バックスラッシュでエスケープされているため)のいずれか一文字にマッチします。 -
;
: セミコロンはリテラルのセミコロンにマッチします。 -
-
#
: シャープ記号はリテラルのシャープ記号にマッチします。
技術的詳細
このコミットの核心は、src/pkg/exp/norm/maketables.go
ファイル内のqcRe
という正規表現の定義の変更にあります。
元の正規表現は以下の通りでした。
var qcRe = regexp.MustCompile(
^([0-9A-F\.]+) ; (NF._QC); ([YNM]) #.*$)
この正規表現の先頭にある^
(キャレット)は、「行の先頭」にマッチするという意味を持ちます。これは、正規表現が対象の文字列の行頭からパターンを探し始めることを強制します。
コミットメッセージによると、「新しい正規表現への移行」が原因で、「テストブロックの最終行が無視される」というバグが発生しました。これは、おそらくテストデータが複数行にわたるブロック形式で記述されており、各行が特定のパターンにマッチすることを期待していたにもかかわらず、新しい正規表現エンジンが^
アンカーの解釈を厳密にしたか、あるいはテストデータのフォーマットが変更されたか、またはその両方が原因で、最終行が「行の先頭」から始まらないと判断され、マッチしなくなったためと考えられます。
例えば、テストブロックの最終行が、前の行の続きとして扱われたり、何らかの理由で正規表現が期待する「行の先頭」の条件を満たさなくなった場合、^
アンカーが存在するとその行はスキップされてしまいます。
この問題を解決するために、コミットではqcRe
正規表現から^
アンカーが削除されました。
変更後の正規表現は以下の通りです。
var qcRe = regexp.MustCompile(
([0-9A-F\.]+) ; (NF._QC); ([YNM]) #.*)
^
アンカーを削除することで、正規表現は行の先頭に限定されずに、文字列内のどこからでもパターンにマッチするようになります。これにより、テストブロックの最終行がたとえ行の途中から始まると見なされても、正規表現がそのパターンを正しく認識し、処理できるようになりました。結果として、テストデータの最終行が無視されるというバグが解消されたわけです。
この修正は、正規表現のアンカーが、特に複数行のテキストを処理する際に、意図しない挙動を引き起こす可能性があることを示しています。正規表現の変更は、その影響範囲を慎重に評価する必要があるという良い例です。
コアとなるコードの変更箇所
--- a/src/pkg/exp/norm/maketables.go
+++ b/src/pkg/exp/norm/maketables.go
@@ -764,7 +764,7 @@ func verifyComputed() {
}
}
-var qcRe = regexp.MustCompile(`^([0-9A-F\\.]+) *; (NF.*_QC); ([YNM]) #.*$`)
+var qcRe = regexp.MustCompile(`([0-9A-F\\.]+) *; (NF.*_QC); ([YNM]) #.*`)
// Use values in DerivedNormalizationProps.txt to compare against the
// values we computed.
コアとなるコードの解説
変更はsrc/pkg/exp/norm/maketables.go
ファイルの766行目にあるqcRe
という変数定義にあります。
-
変更前:
var qcRe = regexp.MustCompile(`^([0-9A-F\\.]+) *; (NF.*_QC); ([YNM]) #.*$`)
この行では、
regexp.MustCompile
関数を使って正規表現をコンパイルし、qcRe
変数に代入しています。正規表現文字列の先頭に^
(行の先頭にマッチするアンカー)が含まれています。 -
変更後:
var qcRe = regexp.MustCompile(`([0-9A-F\\.]+) *; (NF.*_QC); ([YNM]) #.*`)
変更点はこの一行のみで、正規表現文字列の先頭から
^
アンカーが削除されています。これにより、qcRe
は行の先頭に限定されず、入力文字列のどこからでもパターンにマッチするようになります。
この修正により、テストブロックの最終行が、たとえ行の途中から始まるように見えても、正規表現がそのパターンを正しく認識し、処理できるようになりました。これは、テストデータの解析ロジックにおける重要な修正であり、exp/norm
パッケージの正確性を保証するために不可欠でした。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/eef78091932aafec2b8030bc1927699b551d119b
- Go Change List (CL) へのリンク:
https://golang.org/cl/5177052
(このCLは公開情報が見つかりませんでした。リンク切れか、非公開の可能性があります。)
参考にした情報源リンク
- Go言語
regexp
パッケージのドキュメント:- https://pkg.go.dev/regexp
- https://github.com/golang/go/tree/master/src/regexp
- Go言語
exp/norm
パッケージに関する情報 (古い情報):- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHVJqYpBgdE9MMMSnk_RDnHR1-08HTv0d-67HYxQeb6UmTvCvq3UHX0q4T5BOvgPlGML1EGuIwcbJQlfCvYXNgwQgfLYZNjhptJmLnMXwloImU7wEKDhsW8IrLKmZ9oVrVi
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQES8PvSNYo83c6G2zFMzJf3VmTlsYw26Phw-662Nu0r1zVJQb0XHWgZZmg5XZyAKYONvVI4gE9rO32p4X_CGmtWe0UV_UFUI2MSY2kFDN8CZU_yKDwCZCoUYN65iDhN5zHvdDZ8QtaZcyeNioWkQ6VMM8Qq3eIOYiEsg5Bgk-2bzBl3wVZxtNGJl0NHnmBp6DbsDE8eGD80ZW3JteijMeRbWIIygTZvArZXPw==
- Go言語 Unicode正規化 (
golang.org/x/text/unicode/norm
) に関する情報:- https://pkg.go.dev/golang.org/x/text/unicode/norm
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGzG259NXzupmkexDcKRIOAgbXBEFbQD_WZIi8Vxv1XH4PnR4mz3c23K8UKT6WgweBFnzcrUd_bS0TCnhJbLlpCbQ7nraOU-ztWUY6akmx40_is4egzocofM7s3YEoYVg4e7UblEZhdjpDhHaxIw0ux56zDWnWj0JUQ6MnJ_R96oL8=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQHpiDA4BS_OTlrFDZq98VcrCDBGIez7gDIuj1UWrGcT-8C0WUqS-XyN_ZT5oZx4wi5JG9NYplFdVxlL8fMowAncUY067AZsLaSwgLHvKe1-NI1-k54sl2L-2tImSg==
- https://vertexaisearch.cloud.google.google.com/grounding-api-redirect/AUZIYQFLo87Nb0uOb0BA7y84te-v_eJI2qpIQQrvx8JomZowKpENwuUSPIwvpKmTv_PMxkB-7TkHGTqHU4XpTnKHrINnZvblv8EH4TSImQsERQM6u1qd0z7NcNKJlUjyscE8m6rKzMjxdXIMvd8Rudk=