Word & Character Counter — Python source
Live word, character, sentence, and paragraph counts plus reading-time estimate as you type.
This is the Python implementation — the same logic the interactive tool runs, in a shareable, citable form.
"""word-counter — Python
======================
Polyglot showcase port of CosmoDev's "word-counter" tool, ported from the
canonical TypeScript at src/lib/wordCount.ts (the live web library and
unit-test surface).
Display source — part of CosmoDev's polyglot tool pages
(dev.cosmolabs.org), where each tool's pure logic is shown in six
languages side by side. Pure string logic; no cryptographic surface.
Public surface: count_words, count_chars, count_chars_no_spaces,
count_sentences, count_paragraphs, reading_time_min.
"""
from __future__ import annotations
import re
# Compiled once at import — equivalent to the JavaScript literals
# /[^.!?]+[.!?]+/g and /\n{2,}/ used in the canonical implementation.
_SENTENCE_RE = re.compile(r"[^.!?]+[.!?]+")
_PARAGRAPH_RE = re.compile(r"\n{2,}")
def count_words(text: str) -> int:
"""Count whitespace-separated words.
``str.split()`` with no argument splits on runs of Unicode whitespace and
discards leading/trailing empty fields, so empty or whitespace-only input
yields ``[]`` and therefore 0.
"""
return len(text.split())
def count_chars(text: str) -> int:
"""Total character count, whitespace included.
Counts Unicode code points via ``len()``. The canonical TypeScript uses
``.length`` (UTF-16 code units); the two agree for all BMP text and
diverge only for astral symbols (emoji, etc.), which JavaScript counts
as 2.
"""
return len(text)
def count_chars_no_spaces(text: str) -> int:
"""Character count with all whitespace removed (Unicode-aware)."""
return len("".join(ch for ch in text if not ch.isspace()))
def count_sentences(text: str) -> int:
"""Number of sentences: maximal runs of non-terminal characters followed
by one or more sentence terminators (. ! ?).
Text without terminal punctuation therefore counts as 0, matching the
canonical behaviour.
"""
return len(_SENTENCE_RE.findall(text))
def count_paragraphs(text: str) -> int:
"""Number of paragraphs separated by one or more blank lines (2+
newlines). Segments that are blank after stripping do not count."""
return sum(1 for segment in _PARAGRAPH_RE.split(text) if segment.strip())
def reading_time_min(words: int) -> int:
"""Estimated reading time in whole minutes at 200 words per minute.
Returns 0 for empty input, otherwise at least 1. Integer arithmetic —
``(words + 100) // 200`` — reproduces ``round(words / 200)`` exactly for
non-negative word counts and sidesteps Python's banker's rounding, which
would otherwise disagree with JavaScript's ``Math.round`` on exact halves
(e.g. 2.5 → Python 2, JavaScript 3).
"""
if words <= 0:
return 0
return max(1, (words + 100) // 200)
Also available in 13 other languages
Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →