python split 空白はPythonとGoでどう違う?

要点

  • Pythonの引数なしsplit()とGoのFieldsは、測った9種類の入力ですべて同じ結果だった(出典: 自社の実測・2026-10-20)。
  • JavaScriptはtrim()後に空白で分割しても、空文字列では空の要素が1つ残った(出典: 自社の実測・2026-10-20)。
  • Goの正規表現の\sでは、全角スペースとノーブレークスペースを分割できなかった(出典: 自社の実測・2026-10-20)。

「空白で分ける」で何が困るのか

空白で区切った文字列から単語を取り出したいとき、区切り文字に" "を指定する方法と、引数を省略する方法は同じ結果になるとは限らない。連続した空白をひとまとまりとして扱うか、文字列の両端に空の要素を残すかで、返る内容が変わる。

Pythonの引数なしstr.split()とは、空白に従って文字列を分割し、結果から空文字列を除く処理のことだ。

この動作をJavaScriptやGoへ移す際には、「空白」という呼び方だけで処理を選べない。確認する点は、区切りとして認識する文字と、空の要素の扱いの2つだ。通常の空白で分かれることを確認しても、タブや全角スペースで分かれるとは限らない。また、連続空白で空の要素が出なくても、空文字列を渡したときまで空の要素がなくなるとは限らない。

今回の実測では、通常の空白、連続空白、両端の空白、タブ、全角スペース、ノーブレークスペース、改行、空文字列、空白だけの文字列を比較した。

公式の説明では何を区切りとして扱うのか

Pythonのstr.splitのdocstringには、引数の既定値について次の説明がある。

None (the default value) means split according to any whitespace, and discard empty strings from the result.

出典: Python 3.9.0のstr.split.__doc__(取得日 2026-10-20)

ここでは、空白に従って分割することと、空文字列を結果から除くことが明記されている。split(' ')のように区切りを指定した場合と、引数なしの場合を区別する根拠になる。

一方、JavaScriptのString.prototype.split()について、MDNは次のように説明している。

Omitting separator or passing undefined causes split() to return an array with the calling string as a single element.

出典: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split (取得日 2026-10-20)

JavaScriptでは、区切りを省略すると元の文字列を単一の要素とする配列を返す。したがって、Pythonと同じつもりで引数なしのsplit()を書くことはできない。

Goのstrings.Fieldsは、unicode.IsSpaceで定義される連続した空白の周囲で分割し、返す要素を空にしないと説明されている。先頭と末尾の連続した空白を捨てることも明記されている。対してstrings.Splitは、指定した区切りの間にある部分文字列を返す処理だ。

出典: Go 1.26.2のgo doc strings.Fields・go doc strings.Split(取得日 2026-10-20)

連続空白・両端の空白・空文字列を比べる

実測環境はmacOS 26.5.1(BuildVersion 25F80・arm64)、Python 3.9.0、Node v22.22.3、Go 1.26.2だった。同じ9個の入力を各言語に渡し、結果をJSONの形で出力した。以下は、その記録から連続空白、両端の空白、空文字列の結果を抜き出した比較表だ。

分割方法 "a b" " a b " ""
Python s.split() ["a", "b"] ["a", "b"] []
Python s.split(' ') ["a", "", "b"] ["", "a", "b", ""] [""]
Python re.split(r'\s+', s) ["a", "b"] ["", "a", "b", ""] [""]
Node s.split(' ') ["a","","b"] ["","a","b",""] [""]
Node s.split(/\s+/) ["a","b"] ["","a","b",""] [""]
Node s.trim().split(/\s+/) ["a","b"] ["a","b"] [""]
Go strings.Split(s, " ") ["a","","b"] ["","a","b",""] [""]
Go strings.Fields(s) ["a","b"] ["a","b"] []
Goの正規表現 \s+による分割 ["a","b"] ["","a","b",""] [""]

出典: 自社の実測(2026-10-20)

連続空白の入力では、文字としての空白を指定する方法に空の要素が残った。正規表現の\s+では、その入力の途中に空の要素は残らなかったものの、両端に空白がある入力では空の要素が残った。

Nodeでtrim()を先に呼んだ場合、両端の空白がある入力はPythonの引数なしsplit()と同じ要素になった。ただし、空文字列では[""]だった。空白3つだけの入力でも[""]となり、Pythonの[]とは異なった。「両端を取り除けば同じ」と判断する前に、空の入力も比較する必要がある。

どれを使うかは空の要素と文字の範囲で決める

今回の入力に対して、空白をまとめて扱い、空の要素を残さずに分割したいなら、Pythonでは引数なしのsplit()、Goではstrings.Fieldsがその結果を返している。両者は9種類の入力すべてで同じ結果だった。

ただし、この一致をすべての文字へ広げることはできない。追加で1文字ずつ調べたU+001Cでは、Pythonのsplit()は分割したが、GoのFieldsは分割しなかった。Nodeの空白判定でも、この文字は空白として扱われなかった。

正規表現にも違いがある。全角スペースとノーブレークスペースは、PythonのreとNodeの/\s+/では分割されたが、Goの正規表現では分割されなかった。Goのregexp/syntaxに記載された文字クラスの行は次のとおりだ。

\s             whitespace (== [\t\n\f\r ])
[[:space:]]    whitespace (== [\t\n\v\f\r ])

出典: Go 1.26.2のgo doc regexp/syntax(取得日 2026-10-20)

この記述では、\sと[[:space:]]の範囲も異なる。垂直タブはGoの\sには当たらず、実測でも一致しなかった。同じGoで空白を扱う処理でも、Fieldsと正規表現を同じものとして選ぶことはできない。

また、Pythonの正規表現でre.ASCIIを指定した1文字ずつの判定では、全角スペースとノーブレークスペースは\sに一致しなかった。言語名に加え、使う関数や指定した設定まで比較することが必要だ。

出典: 自社の実測(2026-10-20)

自分の環境で確かめる手順

まずPythonの版と、手元のstr.splitの説明を確認する。次のコマンドは、実測記録でも説明の取得に使ったものだ。

python3 -V
python3 -I -c "print(str.split.__doc__)"

分割結果を確認するときは、通常の空白だけでなく、連続空白、両端の空白、空文字列を別々に渡す。全角スペースやノーブレークスペースを扱う入力なら、それらも分けて確かめる。比較する際は、見えている単語だけでなく、空の要素が含まれているかを見る。

ここで掲載した結果は、記載した環境での実測に限られる。Pythonの別の版で結果が変わるか、ブラウザのJavaScriptエンジンでどうなるかは確認できなかった。追加の制御文字についても、U+001C以外のU+001D〜U+001Fなどは測っていない。処理速度も測っていないため、この記事の比較から速さの優劣は判断できない。

出典: 自社の実測(2026-10-20)

コメント

タイトルとURLをコピーしました