Pythonの文字列メソッドcount()は、文字やサブ文字列が何回現れるかを効率よく数えるための基本ツールです。
本記事では初心者がつまずきやすい範囲指定や大小文字、Unicodeの表記ゆれまで丁寧に解説し、すぐ使えるサンプルコードと出力を示しながら理解を深めます。
Pythonのstr.count()の基本
count()で文字列中の出現回数を数える
count()は対象の文字列に対して呼び出し、引数で指定した文字やサブ文字列の出現回数を返します。
まずは最小の例から確認します。
# 基本: "banana" に含まれる "a" の数を数える
s = "banana"
result = s.count("a")
print(result) # 期待: 3
3
戻り値は整数で、見つかった回数を返します。
また、count()は非破壊的です。元の文字列sは変化しません。
- 関連記事:文字列の長さを調べる
文字(1文字)とサブ文字列の両方を数えられる
count()は1文字だけでなく、複数文字からなるサブ文字列にも使えます。
s = "bananana"
print(s.count("a")) # "a" の数
print(s.count("na")) # "na" の数
print(s.count("nana")) # "nana" の数
4
3
2
1文字でも複数文字でも同じメソッドで扱える点が便利です。
見つからない場合は0になる
対象が見つからない場合は常に0になります。
例外にはなりません。
s = "banana"
print(s.count("z")) # "z" は存在しない
print(s.count("xyz")) # "xyz" も存在しない
0
0
count()の引数と範囲指定
引数(sub, start, end)の意味
count()のシグネチャはstr.count(sub[, start[, end]])です。
sub: 数えたい文字またはサブ文字列start: 探索開始位置のインデックス(省略可)end: 探索終了位置のインデックスの直前まで(省略可)
範囲はスライスと同じ半開区間[start, end)で、end自体は含みません。
以下の表は各引数の要点をまとめたものです。
| 引数 | 型 | 既定値 | 説明 |
|---|---|---|---|
| sub | str | なし | 数える対象の文字列 |
| start | int | 0 | 探索開始位置(含む) |
| end | int | len(s) | 探索終了位置(含まない) |
s = "abracadabra"
# 文字列全体を対象に "a" を数える
print(s.count("a"))
# インデックス2から末尾直前までの範囲で "a" を数える
print(s.count("a", 2))
# インデックス2以上、8未満の範囲で "a" を数える
print(s.count("a", 2, 8))
5
4
3
よくある勘違い
endは含まれません。
たとえばcount("a", 2, 8)はインデックス8の位置は対象外です。
部分範囲を指定して数える(startとend)
部分的に数えたい場合、startとendを使います。
文中の特定セクションだけを数えるのに有用です。
text = "Header\nbody line 1\nbody line 2\nFooter"
# 本文部分("body..."が始まる位置以降)だけで "o" を数える
start = text.index("body") # "body" の開始位置
end = text.rindex("\n") # 最後の改行の位置(ここは含まれない)
print(text.count("o", start, end))
2
end = text.rindex("\n")にて最終行が除外されるようになっているので、最後のFooterのoはカウントされず、2個となります。
- 関連記事:find()とindex()で文字列を検索する
- 関連記事:スライスで文字列の一部を取得する
負のインデックスで末尾から範囲指定
インデックスは負の値も使え、末尾からの相対位置を指定できます。
s = "2024-09-10"
# 末尾5文字(例: "-10")の範囲で "-" を数える
print(s.count("-", -5)) # start=-5 から末尾まで
# 末尾8文字の範囲[-8, -3)で "-" を数える
print(s.count("-", -8, -3)) # end は含まれないことに注意
1
1
スライスとcount()の組み合わせ
スライスで切り出した部分に対してcount()を呼ぶ方法も直感的です。
s = "prefix[MIDDLE]suffix"
middle = s[s.index("[")+1 : s.index("]")]
print(middle) # "MIDDLE"
print(middle.count("D"))
MIDDLE
2
スライスは読みやすさの向上にも役立ちます。
具体例で学ぶcount()の使い方
特定の文字数を数える(改行・空白・記号)
ログやテキスト整形では、改行や空白、区切り記号の数を数える場面が多いです。
log = "INFO Start\nWARN Disk low\nERROR Failed\nINFO End\n"
# 改行の数
nl = log.count("\n")
# 半角スペースの数
spaces = log.count(" ")
# コロン ":" の数
colons = log.count(":")
print("newlines:", nl)
print("spaces:", spaces)
print("colons:", colons)
newlines: 4
spaces: 5
colons: 0
改行は可視化されない文字なので、明示的に”\n”を数えると確実です。
サブ文字列の出現回数を数える(単語や拡張子)
単語や拡張子の出現回数を知りたいときにも使えます。
text = "cat scatter catalog concatenate cat"
# "cat" は単語境界を問わず数えるので "scatter" や "catalog" 内も含む
print(text.count("cat"))
files = "a.png b.jpg c.png d.PNG e.png"
# 拡張子 ".png" の出現数(大小文字は区別される点に注意)
print(files.count(".png"))
5
3
単語境界を考慮したい場合は正規表現など別手段が必要です。
- 関連記事:正規表現(re)入門
CSVのカンマ数やURLのスラッシュ数を数える
CSV行の列数はカンマ数+1で見積もれます。
URLの階層をスラッシュで概観することもできます。
csv_line = "id,name,age,email,city"
commas = csv_line.count(",")
columns_estimate = commas + 1 # 簡易見積もり
url = "https://example.com/path/to/resource/"
slashes = url.count("/")
print("commas:", commas)
print("columns (approx):", columns_estimate)
print("slashes:", slashes)
commas: 4
columns (approx): 5
slashes: 6
CSVの厳密な列数は引用符やエスケープに左右されます。
ここでは簡易な見積もりとして活用します。
- 関連記事:CSVを読み込む・書き出す
大文字小文字をそろえてから数える(ケース差への対応)
count()は大小文字を区別します。
事前にlower()やcasefold()で正規化すると意図どおりに数えられます。
text = "Apple, APPLE, apple, ApPlE"
target = "apple"
# そのまま数える(大小文字を区別)
print(text.count(target)) # 1
# 小文字化してから数える
print(text.lower().count(target)) # 4
# より包括的な大小文字正規化(casefold)
print(text.casefold().count(target)) # 4
1
4
4
国際化対応ではcasefold()がより堅牢です。
count()の注意点とベストプラクティス
重なり合う出現は数えない(aa中のaaは1件)
count()は重なり合う一致を数えません。
s = "aaaa"
print(s.count("aa")) # "aa" は [0:2], [2:4] の2回。重なり(1:3)は数えない
2
オーバーラップを数えたい場合は別実装が必要です。
参考: オーバーラップを数える単純実装
def count_overlapping(s: str, sub: str) -> int:
# 1文字ずつずらして一致するたびに数える
if not sub:
return len(s) + 1 # Pythonの仕様に合わせる
c = 0
i = 0
while True:
i = s.find(sub, i)
if i == -1:
return c
c += 1
i += 1 # 1文字進めることでオーバーラップも検出
print(count_overlapping("aaaa", "aa"))
3
空文字はlen(s)+1になる
subに空文字""を渡すと、文字列の「隙間」の数であるlen(s)+1が返ります。
s = "abc"
print(len(s), s.count(""))
3 4
仕様として正しい挙動ですが、多くの場合は意図しないので注意します。
大文字小文字は区別される
デフォルトでは区別ありです。
必要に応じて前処理しましょう。
s = "Hello HELLO HeLLo"
print(s.count("hello")) # 0
print(s.lower().count("hello")) # 3
0
3
全角/半角や結合文字は別扱い(Unicodeの表記ゆれ)
見た目が同じでも、内部表現が違うと一致しません。
例として結合文字を含むケースを示します。
import unicodedata
# 見た目は "é" だが2通りの表現がある
composed = "é" # U+00E9
decomposed = "e\u0301" # "e" + 結合アクセント(U+0301)
s = composed * 3
print("raw count:", s.count(decomposed)) # 0 になる
# 正規化(NFC/NFD)を揃えてから数える
s_nfc = unicodedata.normalize("NFC", s)
sub_nfc = unicodedata.normalize("NFC", decomposed)
print("NFC count:", s_nfc.count(sub_nfc))
raw count: 0
NFC count: 3
Unicode正規化を揃えるのが堅実です。
全角/半角の違いも別文字として扱われます。
正規表現は使えない(パターン一致は対象外)
count()は固定の文字列一致のみです。
パターンや単語境界を扱うにはreモジュールなどを使います。
import re
text = "cat scatter catalog concatenate cat"
# 単語としての "cat" のみ数える(単語境界 \b を利用)
matches = re.findall(r"\bcat\b", text)
print(len(matches))
2
- 関連記事:正規表現(re)入門
大きな文字列では一度だけ数えるようにする(効率化)
巨大な文字列に対して同じcount()を何度も呼ぶと無駄な走査が増えます。
結果を変数に保持し、必要に応じて再利用しましょう。
s = "log entry;" * 1_000_00 # デモ用の大きめ文字列
# 悪い例: 同じ計算を繰り返す
a = s.count(";")
b = s.count(";")
c = s.count(";")
# 良い例: 1回だけ計算して使い回す
count_semicolon = s.count(";")
print(a == b == c == count_semicolon)
True
事前の前処理(例: lower/casefoldや正規化)を1回で済ませてからcountするのも効果的です。
まとめ
str.count()は文字列内の出現回数を素早く数えるための基本メソッドで、単純一致・範囲指定・負のインデックス・スライスとの併用まで押さえれば多くの用途をカバーできます。
注意点として重なり合いは数えない、空文字はlen(s)+1、大小文字やUnicodeの表記ゆれは一致しないことを理解し、必要に応じてlower()やcasefold()、unicodedata.normalize()の前処理を取り入れると良いです。
さらに、大きな文字列では同じ数え上げを繰り返さないなどの効率化も意識しましょう。
これらのポイントを押さえれば、改行や空白、区切り記号、拡張子やURLの構造把握など、実務で頻出のカウント作業を確実にこなせます。
- 関連記事:replaceで文字列を置換する
- 関連記事:str.translateで複数文字を一括置換する
