ログ解析や文章の集計では、文字列の中に特定の文字や語句がいくつ含まれているかを数える場面が頻繁にあります。
Pythonではstr.count()
メソッドを使うと、短いコードで正確にカウントできます。
本記事では、基本から範囲指定、注意点、実用例まで、初心者の方にもわかりやすい順序で丁寧に解説します。
Pythonのcount()の基本
文字列内の特定の文字数を数える
基本構文と返り値
str.count(sub[, start[, end]])
は、文字列の中にsub
(部分文字列)が出現する回数を整数で返します。
単一文字でも複数文字でも指定できます。
サンプルコード
# 文字列中の特定の文字 'a' が何回出るかを数える例
s = "abracadabra"
print(f"文字列: {s}")
print(f"'a' の数: {s.count('a')}")
文字列: abracadabra
'a' の数: 5
補足
count()
は非破壊的です。
元の文字列s
は変更されません。
部分文字列の出現回数を数える
1文字でなくてもOK
1文字だけでなく、短い単語やフレーズなどの「部分文字列」も同様に数えられます。
# 部分文字列 'abra' の出現回数を数える例
s = "abracadabra"
print(f"'abra' の数: {s.count('abra')}")
'abra' の数: 2
注意
重なり合う一致は数えません(詳細は後述)。
例えば"banana".count("ana")
は1になります。
大文字小文字をそろえて数える
lowerでそろえる(英語圏中心の用途に十分)
count()
はケースセンシティブです。
大文字小文字を区別せずに数えたい場合は、対象文字列と検索語の両方をlower()
で小文字化してから数えます。
# 大文字小文字を区別しないカウント
s = "Apple APPLE AppLe"
target = "apple"
print(s.count(target)) # 区別する → 0
print(s.lower().count(target)) # 区別しない → 3
0
3
casefoldでより強力に(Unicodeを意識)
ドイツ語のßなど、特殊なケース変換がある言語も考慮するならcasefold()
が適しています。
# Unicodeに配慮したケース無視のカウント
s = "Straße straße STRASSE"
target = "strasse"
print(s.casefold().count(target)) # 3
3
count()の引数と範囲指定
startとendで範囲を限定して数える
引数の意味と挙動
count(sub, start, end)
のstart
は開始位置(含む)、end
は終了位置(含まない)を0始まりのインデックスで指定します。
スライスと同じ境界規則です。
負のインデックスも使えます。
次の表に要点をまとめます。
引数 | 型 | 既定値 | 意味 |
---|---|---|---|
sub | str | なし | 数えたい部分文字列 |
start | int | 0 | 検索開始位置(含む、負の値可) |
end | int | len(s) | 検索終了位置(含まない、負の値可) |
返り値 | int | — | 非負の整数(一致数) |
# 範囲を限定したカウントの例
s = "abracadabra"
# 先頭から5文字(インデックス0〜4)内の'a'
print("0〜5の範囲での'a':", s.count('a', 0, 5)) # 'abrac' → 2
# インデックス5以降の'a' (end省略で末尾まで)
print("5以降の'a':", s.count('a', 5)) # 'adabra' → 3
# 負のインデックスを使った例(末尾4文字から末尾まで)
print("末尾4文字内の'a':", s.count('a', -4)) # 'abra' → 2
# 範囲外指定は0を返す
print("範囲外指定:", s.count('a', 100, 200)) # → 0
0〜5の範囲での'a': 2
5以降の'a': 3
末尾4文字内の'a': 2
範囲外指定: 0
インデックスの扱いと境界の考え方
endは含まない(スライスと同じ)
end
は含まれません。
count(sub, start, end)
は、概念的にはs[start:end].count(sub)
と同じ範囲を数えます。
# endが含まれないことの確認
s = "abracadabra"
start, end = 2, 9 # s[2:9] は 'racadab'
print("countでの数:", s.count('a', start, end))
print("スライスしてからcount:", s[start:end].count('a'))
countでの数: 3
スライスしてからcount: 3
負のインデックスもスライスと同様
負の値は末尾からの相対位置です。
s = "abracadabra"
print("負のインデックス指定:", s.count('a', -7, -1)) # s[-7:-1] は 'cadabr' → 'a'は2
負のインデックス指定: 2
count()の仕様と注意点
重なり合う一致は数えない
例と結果
count()
は部分文字列の一致が重なる場合、重なり分を数えません。
s = "banana"
print("banana 中の 'ana' の数:", s.count("ana")) # 'ana' は重なって2箇所あるが → 1
banana 中の 'ana' の数: 1
補足
重なり合いも数えたい場合は、別の方法(正規表現の先読みやfind()
を使った1文字ずつのシフト)が必要です。
本記事の範囲ではcount()
の仕様理解に留めます。
空文字を数えたときの結果
仕様
空文字""
は、文字と文字の間に「隙間」として存在すると見なされるため、len(s) + 1
が返ります。
print(" 'abc' に対する空文字のcount:", "abc".count(""))
print(" 空文字に対する空文字のcount:", "".count(""))
'abc' に対する空文字のcount: 4
空文字に対する空文字のcount: 1
Unicodeと正規化の落とし穴
同じ見た目でも実体が異なる
合成文字(例えばé
)には、単一文字(U+00E9)と結合文字(e
+U+0301)の2通りの表現があります。
これらは別物として扱われるため、そのままではcount()
の結果が期待と異なることがあります。
import unicodedata as ud
s1 = "Café" # 単一文字のé (U+00E9)
s2 = "Cafe\u0301" # 'e' + 結合アクセント(U+0301)
print("同一判定:", s1 == s2) # 見た目は同じでもFalse
print("s1の'é'数:", s1.count("é")) # → 1
print("s2の'é'数:", s2.count("é")) # → 0
# 正規化(NFC)してから数える
n1 = ud.normalize("NFC", s1)
n2 = ud.normalize("NFC", s2)
print("正規化後の同一判定:", n1 == n2) # True
print("正規化後の'é'数:", n2.count("é")) # → 1
同一判定: False
s1の'é'数: 1
s2の'é'数: 0
正規化後の同一判定: True
正規化後の'é'数: 1
実務上の指針
外部入力(ファイルやWebフォーム)を扱うときは、比較やカウントの前にunicodedata.normalize()
で正規化するのが安全です。
すぐ使える実用例
複数の文字の合計回数を数える
セットで重複指定を避ける
複数のターゲット文字を合計したいときは、各文字のcount()
を合計します。
同じ文字を重複して渡してしまうと二重カウントになるため、set
で一意化すると安心です。
# 例: 母音(a, e, i, o, u)の合計回数を数える
text = "abracadabra"
targets = ['a', 'e', 'i', 'o', 'u', 'a'] # 'a' を重複して含めてみる
unique_targets = sorted(set(targets)) # 重複排除
counts = {ch: text.count(ch) for ch in unique_targets}
total = sum(counts.values())
print(f"対象: {targets}")
print(f"ユニーク化: {unique_targets}")
print(f"各カウント: {counts}")
print(f"合計: {total}")
対象: ['a', 'e', 'i', 'o', 'u', 'a']
ユニーク化: ['a', 'e', 'i', 'o', 'u']
各カウント: {'a': 5, 'e': 0, 'i': 0, 'o': 0, 'u': 0}
合計: 5
補足
大量データで多数の文字を一度に数える場合はcollections.Counter
なども便利ですが、count()
は「狙った少数の文字や語句を数える」用途にとても手軽です。
改行やタブの数をカウントする
CRLF(Windows改行)の扱いに注意
テキストには"\n"
(LF)、"\r\n"
(CRLF)、"\t"
(タブ)などの制御文字が含まれます。
count()
は文字列そのものを数えるため、"\r\n"
を1つの改行として扱いたいなら、その2文字列を指定する必要があります。
text = "1st line\n2nd line\r\n3rd line\tend"
print("\\n の数:", text.count("\n")) # LFの数 (CRLF中の \n も数える)
print("\\r\\n の数:", text.count("\r\n")) # CRLFのペアとして数える
print("\\t の数:", text.count("\t")) # タブの数
\n の数: 2
\r\n の数: 1
\t の数: 1
実務のヒント
テキストモードでファイルを開く場合、Pythonは既定で改行を"\n"
に正規化します。
そのため、ファイルから読み込んだ文字列に対してはたいていtext.count("\n")
だけで行末の数が把握できます。
リストの各要素にcount()を適用する
ループや内包表記で一括処理
複数の文字列をまとめて処理したいときは、ループやリスト内包表記でcount()
を適用します。
# それぞれの文字列に "ana" が何回出るかを数える
strings = ["banana", "bandana", "cabana", "BANANA"]
target = "ana"
# 要素ごとのカウント(区別する)
for i, s in enumerate(strings, 1):
print(f"{i}. {s!r} -> {s.count(target)}")
# 合計(大文字小文字を区別する/しない)
total_cs = sum(s.count(target) for s in strings) # 区別する
total_ci = sum(s.casefold().count(target) for s in strings) # 区別しない
print(f"合計(区別する): {total_cs}")
print(f"合計(区別しない): {total_ci}")
1. 'banana' -> 1
2. 'bandana' -> 1
3. 'cabana' -> 1
4. 'BANANA' -> 0
合計(区別する): 3
合計(区別しない): 4
メモ
区別しない集計では、"BANANA"
も対象になりますが、count()
は重なり合いを数えないため"banana".count("ana")
は1です(2ではありません)。
まとめ
str.count()
は、狙った文字や部分文字列の出現回数を素早く数えるための、シンプルで強力なメソッドです。
基本の使い方に加えて、start
とend
での範囲指定、end
が含まれないこと、負のインデックスの扱いを理解しておくと自在に使いこなせます。
注意点としては、重なり合う一致を数えないこと、空文字を数えるとlen(s)+1
になる仕様、Unicodeの合成文字による差異があることが挙げられます。
実務では、大文字小文字の統一(lower
/casefold
)やUnicode正規化を組み合わせると、より意図に沿ったカウント結果が得られます。
まずは小さな例から試し、必要に応じて範囲指定や正規化を取り入れて、確実なテキスト集計に役立ててください。