CkText
Job: clean, shape, compare, check and write out everyday text — names, addresses, free-form fields, delimited lists — so the rest of your code never meets raw input.
Text from a database, a form or a payload arrives messy: stray HTML, shouted capitals,
invisible characters, doubled spaces. CkText turns that into something you can safely
store, compare and show. Like every CommonKit class it never throws by default, and its
rejection value is the empty string: non-empty in, empty out means the value was
rejected — check for "" at the call site. Methods that take a fallback run the
fallback through the same cleaning and checks, so a bad fallback comes back empty too.
Quick look
using SafeCoder.CommonKit;
CkText.String(" <b>Hello</b> WORLD "); // -> "Hello WORLD"
CkText.Name("JOHN SMITH"); // -> "John Smith"
CkText.Digits("ID: 12-345 678"); // -> "12345678"
CkText.Slug("Hello, World!"); // -> "hello-world"
CkText.Mask("123-45-6789"); // -> "***-**-6789"
CkText.String("<p></p>"); // -> "" (nothing left — rejected)
CkText.Contains("Hello, World", "world"); // -> true
CkText.IsDigits(" 12345 "); // -> true
CkText.WordCount("<p>Hello</p><p>World</p>"); // -> 2
CkText.Initials("mary-jane o'brien"); // -> "MO"
CkText.Plural(3, "box"); // -> "3 boxes"
CkText.Explain("<p></p>"); // -> "nothing left once cleaned"
Methods
String
The general cleaner.
string String(
string? input = null,
TextCase textCase = TextCase.None,
int maxLength = CkSettings.MaxTextInputLength,
bool clearEscape = false,
bool normalize = false,
bool decodeEntities = true,
bool truncate = true,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value to clean. |
textCase |
TextCase |
TextCase.None |
The casing to apply; None leaves casing alone. |
maxLength |
int |
CkSettings.MaxTextInputLength (50,000) |
Maximum length of the cleaned value, measured after markup removal and with outer whitespace set aside — the padding of a fixed-width column is not content. Values below 1 fall back to the default. |
clearEscape |
bool |
false |
Flatten line breaks and tabs to spaces. |
normalize |
bool |
false |
Replace every character that is not an ASCII letter, digit or whitespace with _ (runs collapse to one), and collapse every whitespace run to a single space. |
decodeEntities |
bool |
true |
Decode HTML entities as part of markup removal, so & becomes &. Pass false to strip tags but leave entity text alone — for a URL, where ® in a query string is a value, not markup. |
truncate |
bool |
true |
Cut an over-long value to fit (the default) instead of rejecting it. The cut runs last and never splits a surrogate pair. |
fallback |
string? |
null |
Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) cleans to usable text |
The cleaned text: "Hello WORLD" |
The cleaned text: "Hello WORLD" |
Rejected: missing, nothing left once cleaned, or over maxLength with truncate off |
Empty string | Throws CkTextException — carries Input, Expected and Reason |
Markup is stripped with patterns, not truly parsed — see Html for the caveats.
String is the general cleaner. It strips markup (HTML tags), decodes entities
(& becomes &), removes control and invisible characters, applies the casing you
ask for, then tidies whitespace. Switches: clearEscape flattens line breaks and tabs
to spaces, normalize replaces everything that is not an ASCII letter, digit or
whitespace with _, decodeEntities: false leaves entity text alone (useful for URLs,
where ® is a value, not markup), and truncate: false rejects an over-long value
instead of cutting it to maxLength.
CkText.String(" <b>Hello</b> WORLD "); // -> "Hello WORLD"
CkText.String(
input: "JOHN DOE",
textCase: TextCase.Lowercase
); // -> "john doe"
The textCase parameter is a TextCase enum (from SafeCoder.CommonKit.Enums): None
(the default — leave casing alone), Lowercase, Uppercase, Titlecase (capitalize
every word), Sentencecase (capitalize every sentence), Capitalize (first letter
only), and TitlecaseUnlessMixed (title-case only when the value arrives all-caps or
all-lowercase).
Name
A person or company name.
string Name(
string? input = null,
int maxLength = CkSettings.MaxNameInputLength,
bool normalize = false,
TextCase textCase = TextCase.TitlecaseUnlessMixed,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw name value. |
maxLength |
int |
CkSettings.MaxNameInputLength (255) |
Maximum length of the cleaned name, measured after markup removal. An over-long name is truncated, not rejected. |
normalize |
bool |
false |
Replace every character that is not an ASCII letter, digit or whitespace with _. Off by default — it would turn "O'Brien" into "O_Brien"; enable only when an ASCII-safe value is required. |
textCase |
TextCase |
TextCase.TitlecaseUnlessMixed |
The casing to apply: title-case only a value that arrives all-caps or all-lowercase. Pass TextCase.Titlecase to re-case mixed input mechanically, or TextCase.None to leave casing alone. |
fallback |
string? |
null |
Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) cleans to a name |
The cleaned name: "John Smith" |
The cleaned name: "John Smith" |
| Rejected: missing, or nothing left once cleaned | Empty string | Throws CkTextException — carries Input, Expected and Reason |
Title-casing is mechanical: "mcdonald" becomes "Mcdonald", not "McDonald".
Name cleans a person or company name. Its default casing is
TextCase.TitlecaseUnlessMixed: shouting is tidied, but a deliberate mix like
"McDonald" is kept exactly as typed. Over-long names are truncated rather than
rejected — a clipped name is more useful than none.
CkText.Name("JOHN SMITH"); // -> "John Smith"
CkText.Name("o'brien-smith"); // -> "O'Brien-Smith"
CkText.Name("John McDonald"); // -> "John McDonald" (mixed case — kept as typed)
Address
A multi-line postal address.
string Address(
string? input = null,
int maxLength = CkSettings.MaxAddressInputLength,
TextCase textCase = TextCase.None,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw address value, one line per address line. |
maxLength |
int |
CkSettings.MaxAddressInputLength (1,024) |
Maximum length of the cleaned address, measured after markup removal. Values below 1 fall back to the default. |
textCase |
TextCase |
TextCase.None |
The casing to apply; None preserves what the user typed. Titlecase tidies shouted input but also lower-cases meaningful capitals, turning "PO BOX 12 NW" into "Po Box 12 Nw". |
fallback |
string? |
null |
Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) cleans to an address |
The cleaned lines joined with \n: "1 Main St\nSpringfield" |
The cleaned lines joined with \n: "1 Main St\nSpringfield" |
| Rejected: missing, nothing left once cleaned, or over the limit | Empty string | Throws CkTextException — carries Input, Expected and Reason |
Presentation cleanup, not verification — nothing confirms the street, city or country exists, and surplus lines beyond CkSettings.MaxAddressLines (8) are discarded.
Address cleans a multi-line postal address. Line breaks are kept but reduced to
one per line, each line is trimmed, blank lines are dropped, and lines are joined with
\n whatever the input used. Casing is left alone by default, because address
abbreviations like "NW" mean something.
CkText.Address(" 1 Main St \n\n Springfield "); // -> "1 Main St\nSpringfield"
Html
The plain text out of markup.
string Html(
string? input = null,
int maxLength = CkSettings.MaxTextInputLength,
bool truncate = true,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw HTML, or plain text. |
maxLength |
int |
CkSettings.MaxTextInputLength (50,000) |
Maximum length of the extracted text. Values below 1 fall back to the default. |
truncate |
bool |
true |
Cut over-long text to fit (the default) instead of rejecting it. The cut never splits a surrogate pair. |
fallback |
string? |
null |
Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) holds readable text |
The extracted text: "Save & close" |
The extracted text: "Save & close" |
Rejected: missing, nothing left once the markup is gone, or over the limit with truncate off |
Empty string | Throws CkTextException — carries Input, Expected and Reason |
Text extraction, not an XSS boundary — markup is matched with patterns, and an unclosed <script or <!-- swallows the rest of the value.
Html extracts the plain text from HTML: script and style bodies are discarded,
block tags become line breaks, other tags become spaces, entities are decoded, and every line ending (\r\n, a bare \r) comes out as \n. Every
other cleaner runs this step first — call it directly only when you want the text of an
HTML value and nothing else done to it.
CkText.Html("<p>Save & close</p>"); // -> "Save & close"
CkText.Html("<script>alert(1)</script>Done"); // -> "Done"
Digits
Only the digits.
string Digits(
string? input = null,
int minLength = 1,
int maxLength = CkSettings.MaxDigitsLength,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
minLength |
int |
1 |
Fewest digits accepted; values with fewer are rejected. Values below 1 count as 1. |
maxLength |
int |
CkSettings.MaxDigitsLength (64) |
Most digits accepted; values with more are rejected. Values below 1 fall back to the default. |
fallback |
string? |
null |
Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) yields an in-range digit count |
The digits: "12345678" |
The digits: "12345678" |
| Rejected: missing, oversized, a contradictory range, or a digit count outside it | Empty string | Throws CkTextException — carries Input, Expected and Reason |
Only ASCII 0–9 count — Arabic-Indic, fullwidth and other Unicode digits are dropped like any other character.
Digits keeps only the ASCII digits 0–9 — for account, policy and member numbers
that arrive with prefixes or separators. minLength and maxLength bound the digit
count; an out-of-range count is rejected, never truncated, so a mangled number can
never pass as a valid one.
CkText.Digits("ID: 12-345 678"); // -> "12345678"
CkText.Digits("$1,234.56"); // -> "123456" (not an amount any more!)
Truncate
Cut to a length.
string Truncate(
string? input = null,
int maxLength = CkSettings.MaxTextInputLength,
bool atWordBoundary = false,
string? ellipsis = null,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The value to shorten. |
maxLength |
int |
CkSettings.MaxTextInputLength (50,000) |
Maximum length of the result, ellipsis included. |
atWordBoundary |
bool |
false |
Move the cut back to the last whitespace inside the kept part, so no word is chopped in half. A value with no whitespace before the limit is cut mid-word. |
ellipsis |
string? |
null |
Text appended when the value was shortened, e.g. "…" or "...". Null or empty appends nothing. |
fallback |
string? |
null |
Backup value used when the result would be empty. It gets the same trim-and-shorten treatment; a fallback that is itself rejected answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) fits, or can be cut to fit |
The trimmed value unchanged, or the shortened value: "hello" |
The same |
Missing, maxLength below 1, or the ellipsis alone would not fit |
Empty string | Throws CkTextException — carries Input, Expected and Reason |
The cut never splits a surrogate pair — one emoji is two characters, kept together.
Truncate cuts a value down to at most maxLength characters. It does not clean —
it only trims surrounding whitespace and shortens. An optional ellipsis is appended
within the limit, and atWordBoundary: true moves the cut back so no word is chopped
in half. A cut is never an error: throwIfInvalid: true fires only when nothing usable
came in — no value, a limit below 1, an ellipsis that would not fit.
CkText.Truncate(
input: "hello world",
maxLength: 5
); // -> "hello"
CkText.Truncate(
input: "hello world",
maxLength: 5,
ellipsis: "…"
); // -> "hell…" (the ellipsis counts toward the limit)
CkText.Truncate(
input: "the quick brown fox",
maxLength: 12,
atWordBoundary: true
); // -> "the quick"
Mask
Hide all but the last few.
string Mask(
string? input = null,
int keepLast = CkSettings.DefaultMaskKeepLast,
char maskChar = '*'
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The sensitive value. It is not cleaned or validated first — mask what you have. |
keepLast |
int |
CkSettings.DefaultMaskKeepLast (4) |
How many trailing letters and digits stay visible; negatives count as 0. |
maskChar |
char |
'*' |
The replacement character. Whitespace and control characters fall back to '*'. |
Returns
| When | You get |
|---|---|
| The value has content | The masked value: "***-**-6789" — a value with no more letters and digits than keepLast is masked completely |
| Missing or whitespace-only | Empty string — and it never throws |
The result still reveals the length and separator layout — the usual trade-off for a recognisable "ends in 6789".
Mask hides a value for display or logging: every letter and digit except the last
keepLast (4 by default) becomes maskChar ('*' by default), while spaces and
punctuation stay, so the shape is still recognisable. A value with no more letters and
digits than keepLast is masked completely — a short value never leaks whole. The
value is not cleaned first: mask what you have.
CkText.Mask("123-45-6789"); // -> "***-**-6789"
CkText.Mask("4111 1111 1111 1234"); // -> "**** **** **** 1234"
CkText.Mask("42"); // -> "**" (too short to show anything)
Slug
A key for urls and file names.
string Slug(
string? input = null,
char separator = '-',
int maxLength = CkSettings.MaxSlugLength,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
separator |
char |
'-' |
Placed between words. Letters, digits, whitespace and control characters fall back to '-'. |
maxLength |
int |
CkSettings.MaxSlugLength (100) |
Maximum length of the slug; a cut lands on the last separator inside the limit where one exists. Values below 1 fall back to the default. |
fallback |
string? |
null |
Backup value used when the result would be empty. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) keeps a letter or digit |
The slug: "hello-world" |
The slug: "hello-world" |
| Rejected: missing, or no letter or digit survives | Empty string | Throws CkTextException — carries Input, Expected and Reason |
Common special letters transliterate, not just fold — "Straße" becomes "strasse".
Slug turns a value into a slug — lower-case ASCII letters and digits with a
single separator ('-' by default) between words — for URLs, file names and lookup
keys. Accents fold to their base letter, apostrophes are dropped, and everything else
acts as a word boundary. A cut for maxLength lands on the last separator inside the
limit where one exists, so no word is left half-finished.
CkText.Slug("Hello, World!"); // -> "hello-world"
CkText.Slug("Café Déjà Vu"); // -> "cafe-deja-vu"
CkText.Slug("don't"); // -> "dont"
Join and Split are round-trip twins for the classic "list in one column" pattern.
Join
A list into one string.
string Join(
string? separator,
params IEnumerable<string?> parts
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
separator |
string? |
— | Placed between kept parts; null means no separator. |
parts |
params IEnumerable<string?> |
— | The candidate parts; null, empty and whitespace-only ones are skipped. |
Returns
| When | You get |
|---|---|
| At least one part has content | The joined value: "1 Main St, Springfield" |
No part has content, or parts is null |
Empty string — an answer, not a failure; it never throws |
Join trims but does not otherwise clean — pass untrusted parts through String or the right validator first.
Join joins the non-empty parts with a separator, trimming each part and
skipping nulls, empties and whitespace-only parts — no conditionals needed at the call
site.
CkText.Join(
separator: ", ",
parts: ["1 Main St", "", null, " Springfield "]
); // -> "1 Main St, Springfield"
Split
One string into a list.
string[] Split(
string? input = null,
string? separator = ","
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The delimited text. |
separator |
string? |
"," |
What separates the pieces; a comma by default. |
Returns
| When | You get |
|---|---|
| The text splits into usable pieces | The cleaned pieces, empties dropped: ["urgent", "manual-review"] |
No text, no separator, or text over CkSettings.MaxTextInputLength (50,000) characters |
Empty list — never null, and it never throws |
Exception-free, like Join — there is nothing here that can fail.
Split goes the other way: it splits on the separator (a comma by default),
cleans each piece the way String cleans one value, and drops the pieces that come out
empty. Split never returns null — no usable input yields an empty array.
CkText.Split("urgent, , manual-review"); // -> ["urgent", "manual-review"]
CkText.Split(
input: "a|b||c",
separator: "|"
); // -> ["a", "b", "c"]
SameText
The same text, ignoring the noise.
bool SameText(
string? a = null,
string? b = null,
double minSimilarity = 1.0
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
a |
string? |
null |
The first value. |
b |
string? |
null |
The second value. |
minSimilarity |
double |
1.0 |
The Similarity score, 0 to 1, at or above which the values count as the same; 1 means an exact match after cleanup. Values above 1 or NaN act as 1; values at or below 0 match everything. |
Returns
| When | You get |
|---|---|
The values are the same text once cleaned — or score at least minSimilarity |
true |
| They differ, or only one of them cleans to nothing | false — an answer, not a failure; it never throws |
Two values that both clean to nothing — null and empty included — count as the same.
SameText tells whether two values are the same text once markup, whitespace,
punctuation, casing and accents are ignored. Digits keep their separators, so "1.5" and
"15" stay different. Pass minSimilarity below 1 to make the comparison fuzzy.
CkText.SameText(
a: "John Smith",
b: "john smith"
); // -> true
CkText.SameText(
a: "Café",
b: "cafe"
); // -> true
CkText.SameText(
a: "Jon Smith",
b: "John Smith",
minSimilarity: 0.85
); // -> true (the two are 0.888… similar — one edit in nine letters)
Similarity
How alike two values are.
double Similarity(
string? a = null,
string? b = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
a |
string? |
null |
The first value. |
b |
string? |
null |
The second value. |
Returns
| When | You get |
|---|---|
| Both values clean to something | The score, 0 to 1: 0.75 |
| Both clean to nothing | 1.0 — nothing differs from nothing |
| One cleans to nothing, the other does not | 0.0 — and it never throws |
Values longer than CkSettings.MaxSimilarityInputLength (4,000) characters after cleanup are only tested for equality, scoring 1 or 0.
Similarity scores how alike two values are, from 0 (nothing in common) to 1 (the
same text), after the same cleanup as SameText.
The score comes from the edit distance — the fewest single-character changes that turn
one value into the other — divided by the longer length, measured on the letters and
digits alone. Two values that both clean to nothing score 1; one empty against one
non-empty scores 0.
CkText.Similarity(
a: "abcd",
b: "abcx"
); // -> 0.75 (one change in four letters)
Useful for "is this the same person?" checks where source systems disagree on spelling. As a rule of thumb, 0.9 tolerates one typo in a ten-character name.
CommonKit reports "nothing" as "", but a database column usually wants NULL — the
next two methods are the bridge.
IsEmpty
Is this field filled in?
bool IsEmpty(
string? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
Returns
| When | You get |
|---|---|
| Null, empty, whitespace only, markup with nothing inside, invisible characters alone — anything String would clean to nothing | true |
| Any character survives the cleaning | false |
IsEmpty is the bool twin of NullIfEmpty and ToStringOrNull:
the same verdict, as a yes or no. "0" and "false" are text, so they are not empty.
CkText.IsEmpty("<p></p>"); // -> true
CkText.IsEmpty(" "); // -> true (decodes to whitespace)
CkText.IsEmpty("0"); // -> false
HasLength
Between so many and so many characters.
bool HasLength(
string? input = null,
int min = 1,
int max = CkSettings.MaxTextInputLength
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
min |
int |
1 |
The smallest length allowed. Below 0 acts as 0. |
max |
int |
CkSettings.MaxTextInputLength (50,000) |
The largest length allowed — the same cap String applies by default. Below 0 acts as 0. |
Returns
| When | You get |
|---|---|
The cleaned length is at least min and at most max, both ends included |
true |
Shorter, longer, or min and max contradict each other |
false |
The value is cleaned first as String would — markup stripped, invisible
characters removed, whitespace tidied — so the length measured is that of the text you
would store. It is counted the way CharacterCount counts: one Unicode
code point per character, so an emoji counts once. With the default min of 1 an empty
value is false; pass min: 0 to accept it.
CkText.HasLength(
input: "hello",
min: 3,
max: 10
); // -> true
CkText.HasLength(
input: "<b>hi</b>",
min: 2,
max: 2
); // -> true (measured after cleanup)
CkText.HasLength("🙂🙂", 2, 2); // -> true (two characters, not four)
StartsWith
Does it begin with this?
bool StartsWith(
string? input = null,
string? value = null,
bool ignoreCase = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
value |
string? |
null |
The beginning to look for. |
ignoreCase |
bool |
true |
Match regardless of case. Pass false for an exact match. |
Returns
| When | You get |
|---|---|
| The cleaned value begins with the cleaned text | true |
| It does not, or either side cleans to nothing | false |
Both sides are cleaned as Contains cleans them, so leading whitespace or
markup never hides the beginning. Accents are kept: "Café" does not start with "cafe".
CkText.StartsWith("INV-2024-001", "inv-"); // -> true
CkText.StartsWith(" <b>INV</b>-1", "inv"); // -> true
CkText.StartsWith("hello", ""); // -> false (nothing to look for)
EndsWith
Does it end with this?
bool EndsWith(
string? input = null,
string? value = null,
bool ignoreCase = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
value |
string? |
null |
The ending to look for. |
ignoreCase |
bool |
true |
Match regardless of case. Pass false for an exact match. |
Returns
| When | You get |
|---|---|
| The cleaned value ends with the cleaned text | true |
| It does not, or either side cleans to nothing | false |
The mirror of StartsWith, with the same cleaning and the same rules.
CkText.EndsWith("report.PDF", ".pdf"); // -> true
CkText.EndsWith("<p>hello world</p>", "world"); // -> true
CkText.EndsWith(
input: "Hello",
value: "LO",
ignoreCase: false
); // -> false
Contains
Does it hold this anywhere?
bool Contains(
string? input = null,
string? value = null,
bool ignoreCase = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value to search. |
value |
string? |
null |
The text to look for. |
ignoreCase |
bool |
true |
Match regardless of case. Pass false for an exact match. |
Returns
| When | You get |
|---|---|
| The cleaned value holds the cleaned text | true |
| It does not, or either side cleans to nothing | false |
Both sides are cleaned first as String would — markup stripped, invisible
characters removed — and line breaks and whitespace runs are flattened to one space, so
"<b>Hello</b>\nWorld" holds "hello world". Accents are kept: "Café" does not hold
"cafe" — use SameText or InList for an accent-blind comparison.
An empty needle finds nothing, unlike the framework's own string.Contains, which says
every string contains "".
CkText.Contains("Hello, World", "world"); // -> true
CkText.Contains("<b>Hello</b>\nWorld", "hello world"); // -> true
CkText.Contains("Café", "cafe"); // -> false (accents are kept)
CkText.Contains("abc", ""); // -> false
ContainsAny
Does it hold any of these?
bool ContainsAny(
string? input = null,
params IEnumerable<string?> values
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value to search. |
values |
params IEnumerable<string?> |
— | The pieces of text to look for. Null, empty and blank entries are skipped. |
Returns
| When | You get |
|---|---|
| The cleaned value holds at least one entry | true |
| It holds none, the list is empty, or the value cleans to nothing | false |
The block-list check for comments and free text: ContainsAny(comment, "buy now",
"free money"). Each side is cleaned as Contains cleans it and the same rules
apply. A params list must be the last parameter, so there is no ignoreCase — the
method always ignores case; call Contains once per entry when case matters.
CkText.ContainsAny("Buy now!!!", "buy now", "free money"); // -> true
CkText.ContainsAny("hello", "x", "y"); // -> false
CkText.ContainsAny("hello"); // -> false (nothing to look for)
InList
Is it one of the allowed values?
bool InList(
string? input = null,
params IEnumerable<string?> allowed
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
allowed |
params IEnumerable<string?> |
— | The values to accept. Null, empty and blank entries are skipped. |
Returns
| When | You get |
|---|---|
| The value matches an entry under SameText rules | true |
| It matches none, the list is empty, or the value cleans to nothing | false |
The allow-list check for status fields, codes and any column that may hold only certain
words. Values are compared the way SameText compares — markup, whitespace,
punctuation, casing and accents ignored — so "Active ", "ACTIVE" and "<b>active</b>"
all match "active". The comparison is exact after cleanup; use SameText with
minSimilarity to tolerate typos. Unlike SameText, an empty value never matches an
empty entry here. Because the comparison keeps letters and digits alone, a value made of
symbols only, "🙂" or "--", matches nothing, not even itself.
CkText.InList("Active ", "active", "inactive"); // -> true
CkText.InList("pending", "active", "inactive"); // -> false
CkText.InList("1.5", "15"); // -> false (separators between digits matter)
IsDigits
Digits only.
bool IsDigits(
string? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
Returns
| When | You get |
|---|---|
| At least one character survives the cleaning and every one is an ASCII digit, 0 to 9 | true |
| Anything else — a space, a sign, a decimal point, another script's digits, an empty value | false |
A shape check for identifiers, PINs and postal codes, not a number check: a quantity or an amount is read with CkNumber or CkMoney, and the digits of a formatted value are pulled out with Digits.
CkText.IsDigits("012345"); // -> true
CkText.IsDigits(" 42 "); // -> true (cleaned first)
CkText.IsDigits("12.5"); // -> false
CkText.IsDigits("-1"); // -> false
IsLetters
Letters only.
bool IsLetters(
string? input = null,
bool allowSpaces = false
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
allowSpaces |
bool |
false |
Let spaces sit between the letters. Line breaks and tabs count as spaces. |
Returns
| When | You get |
|---|---|
| At least one character survives the cleaning and every one is a letter (or an allowed space) | true |
| Anything else — a digit, a hyphen, an apostrophe, an empty value, spaces alone | false |
Letters of any script: "José" and "東京" pass, "R2D2" and "Anne-Marie" do not.
For a person's name, which may carry hyphens and apostrophes, use Name instead.
CkText.IsLetters("José"); // -> true
CkText.IsLetters("hello world"); // -> false
CkText.IsLetters(
input: "hello world",
allowSpaces: true
); // -> true
IsAlphanumeric
Letters and digits only.
bool IsAlphanumeric(
string? input = null,
bool allowSpaces = false
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
allowSpaces |
bool |
false |
Let spaces sit between the characters. Line breaks and tabs count as spaces. |
Returns
| When | You get |
|---|---|
| At least one character survives the cleaning and every one is a letter or a digit (or an allowed space) | true |
| Anything else — punctuation, an underscore, an empty value, spaces alone | false |
For user names, codes and keys that must survive as identifiers. Letters and digits of
any script count, so "Ünal2024" passes; "user_name" does not.
CkText.IsAlphanumeric("abc123"); // -> true
CkText.IsAlphanumeric("user_name"); // -> false
CkText.IsAlphanumeric("abc 123"); // -> false (unless allowSpaces: true)
IsAscii
Will this survive a legacy system?
bool IsAscii(
string? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
Returns
| When | You get |
|---|---|
| At least one character survives the cleaning and every one is below 128 | true |
| An accented letter, an emoji, a curly quote — or an empty value | false |
The value is cleaned first as String would, so an entity such as é is
decoded before it is judged, and invisible characters never tip the verdict.
CkText.IsAscii("Hello"); // -> true
CkText.IsAscii("Café"); // -> false
CkText.IsAscii("é"); // -> false (decoded to é first)
IsUpper
All capitals?
bool IsUpper(
string? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
Returns
| When | You get |
|---|---|
| The cleaned value holds at least one cased letter and none of them is lower case | true |
| A lower-case letter is present, or there is no cased letter at all | false |
Digits, punctuation, whitespace and letters without a case — Chinese characters, say — do
not count either way, so "HELLO WORLD 42" passes while "42" does not. Handy for
spotting shouted input before Name or String recases it.
CkText.IsUpper("HELLO WORLD 42"); // -> true
CkText.IsUpper("Hello"); // -> false
CkText.IsUpper("42"); // -> false (nothing has a case)
IsLower
All lower case?
bool IsLower(
string? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
Returns
| When | You get |
|---|---|
| The cleaned value holds at least one cased letter and none of them is upper case | true |
| An upper-case letter is present, or there is no cased letter at all | false |
The mirror of IsUpper, with the same rules.
CkText.IsLower("hello world 42"); // -> true
CkText.IsLower("Hello"); // -> false
Matches
Does it fit this pattern?
bool Matches(
string? input = null,
string? pattern = null,
bool ignoreCase = false,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
pattern |
string? |
null |
The .NET regular expression to test against. |
ignoreCase |
bool |
false |
Let letters match regardless of case. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering false, when the pattern is missing, invalid, or runs out of time. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The cleaned value matches the pattern | true |
true |
| It does not match, or cleans to nothing | false |
false — no match is an answer, not an error |
| Rejected: no pattern, not a valid pattern, or the pattern ran out of time | false |
Throws CkTextException — carries Input, Expected and Reason |
The escape hatch for a shape none of the other checks describes — a product code, a
licence plate, a house-style reference number. The value is cleaned first as
String would, so the pattern meets the text you would store rather than the
raw markup around it. The pattern runs culture-invariant under the library's regular
expression timeout (CkSettings.RegexTimeoutMs). Anchor it with ^ and $ when the
whole value must match; without them a match anywhere counts. The flag concerns the
pattern, never the value: a value that does not match answers false either way, which is
what makes the method safe to call per row.
Regular expression dialects differ at the edges. The common subset — character classes, quantifiers, anchors, groups and alternation — reads the same in .NET, PCRE and JavaScript; named groups, Unicode categories and look-behind do not always travel. Keep a pattern that must be shared between systems inside that subset.
CkText.Matches("AB-1234", @"^[A-Z]{2}-[0-9]{4}$"); // -> true
CkText.Matches("<b>AB-1234</b>", @"^[A-Z]{2}-[0-9]{4}$"); // -> true (cleaned first)
CkText.Matches(
input: "ab-1234",
pattern: @"^[A-Z]{2}-[0-9]{4}$",
ignoreCase: true
); // -> true
CkText.Matches("abc", "[bad"); // -> false (a bad pattern is not a match)
WordCount
How many words?
int WordCount(
string? input = null,
bool stripHtml = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
stripHtml |
bool |
true |
Count the visible text once markup is gone. Pass false to count the value untouched, tags and all. |
Returns
| When | You get |
|---|---|
| The value holds words | The count: 4 |
| Null, empty, or nothing with a letter or digit in it | 0 — an answer, not a failure; it never throws |
A word is a run of non-whitespace characters holding at least one letter or digit, so
"well-known" is one word, "a@b.com" is one, and a lone dash or "..." is none. With
markup stripped, the default, block tags become the boundary: "<p>Hello</p><p>World</p>"
is 2. With stripHtml: false the same input is 1, because nothing separates it. Words are
split on whitespace alone, which suits languages that space their words; a run of Chinese
or Japanese text without spaces counts as one word.
CkText.WordCount("The quick brown fox"); // -> 4
CkText.WordCount("<p>Hello</p><p>World</p>"); // -> 2
CkText.WordCount(
input: "<p>Hello</p><p>World</p>",
stripHtml: false
); // -> 1
CharacterCount
How many characters?
int CharacterCount(
string? input = null,
bool stripHtml = true
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
stripHtml |
bool |
true |
Count the visible text once markup is gone and the value is tidied. Pass false to count the value untouched, spaces and tags included. |
Returns
| When | You get |
|---|---|
| The value holds characters | The count: 13 |
| Null, empty, or — with markup stripped — a value that cleans to nothing | 0 — an answer, not a failure; it never throws |
By default the value is cleaned as String would — entities decoded, control and
invisible characters removed, whitespace collapsed — so the count is that of the text a
reader sees and you would store. Either way one character is one Unicode code point, so an
emoji or an accented letter counts once. That is what most other languages and databases
call a character count; the maxLength limits elsewhere in this class count UTF-16 units,
where an emoji counts twice. HasLength counts the same way as this method.
CkText.CharacterCount("Hello, world!"); // -> 13
CkText.CharacterCount("<p>Hello</p>"); // -> 5
CkText.CharacterCount(
input: "<p>Hello</p>",
stripHtml: false
); // -> 12
CkText.CharacterCount("Café 🙂"); // -> 6
NullIfEmpty
Turn empty into null.
string? NullIfEmpty(
string? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The value, typically the result of another CommonKit call. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException instead of answering null when the value is missing, empty or whitespace-only. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value has content | The value unchanged: "hello" |
The value unchanged: "hello" |
| Missing, empty or whitespace-only | null |
Throws CkTextException — carries Input, Expected and Reason |
No trimming or cleaning is applied — the value comes back exactly as it went in.
NullIfEmpty is the bridge: null for a missing, empty or whitespace-only value,
the value untouched otherwise. Wrap any CommonKit string call in it on the way to
storage — CkText.NullIfEmpty(CkEmail.Address(raw)) stores a rejected email as NULL,
not as "". That is the pattern for every string class; there are deliberately no
EmailOrNull-style methods.
CkText.NullIfEmpty(""); // -> null
CkText.NullIfEmpty("hello"); // -> "hello"
ToStringOrNull
Clean, then null when nothing is left.
string? ToStringOrNull(
string? input = null,
TextCase textCase = TextCase.None,
int maxLength = CkSettings.MaxTextInputLength,
bool clearEscape = false,
bool normalize = false,
bool decodeEntities = true,
bool truncate = true,
string? fallback = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value to clean. |
textCase |
TextCase |
TextCase.None |
The casing to apply. |
maxLength |
int |
CkSettings.MaxTextInputLength (50,000) |
Maximum length of the cleaned value; see String. |
clearEscape |
bool |
false |
Flatten line breaks and tabs to spaces. |
normalize |
bool |
false |
Replace every character that is not an ASCII letter, digit or whitespace with _; see String. |
decodeEntities |
bool |
true |
Decode HTML entities as part of markup removal; see String. |
truncate |
bool |
true |
Cut an over-long value to fit (the default) instead of rejecting it. |
fallback |
string? |
null |
Backup value used when the result would be null. It goes through the same cleaning and checks as input; a fallback that is itself rejected answers null. |
Returns
| When | You get |
|---|---|
The value (or the fallback) cleans to usable text |
The cleaned text: "hi" |
Missing, nothing left once cleaned, or over the limit with truncate off |
null — this method never throws |
Exactly String's cleaning — only the "nothing" answer changes, from "" to null.
ToStringOrNull combines String and
NullIfEmpty in one call: it cleans exactly
as String does (same parameters), then answers null instead of "" when nothing
remains — for a value headed to a nullable column or a JSON property.
CkText.ToStringOrNull(" <b>hi</b> "); // -> "hi"
CkText.ToStringOrNull("<p></p>"); // -> null
When data leaves your process, write it with the next two methods instead of raw
ToString(). They use the invariant culture — a fixed, machine-neutral format — so a
server set to a European locale never writes "1234,5" with a decimal comma.
NumberToString
A number written out safely.
string NumberToString(
object? input = null,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value to convert, typically boxed by a data reader or deserializer. |
fallback |
string? |
null |
Backup value used when the result would be empty. Being text, it must parse as a plain number in the invariant culture and is then formatted like input, never echoed as typed: "007" answers "7", and "N/A" answers "". |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
The value (or the fallback) is a finite number |
The plain invariant form: "1234.5" — never "1234,5" |
The plain invariant form: "1234.5" — never "1234,5" |
| Unreadable: null, not a numeric type (bools and chars included), or not finite | Empty string | Throws CkTextException — carries Input, Expected and Reason |
Booleans and chars are rejected even though they convert numerically — treating them as numbers is almost always a bug.
NumberToString converts a boxed numeric value to its plain string form: no
padding, no grouping, no currency symbol. Floating-point values round-trip cleanly.
Native-sized nint/nuint are written as the value they hold, whatever the process bitness.
Booleans and chars are rejected — treating them as numbers is almost always a bug. To
read a value as a number, that is CkNumber's job.
CkText.NumberToString(1234.5); // -> "1234.5" (never "1234,5")
CkText.NumberToString(0.1f); // -> "0.1" (not "0.100000001490116")
CkText.NumberToString(19.99m); // -> "19.99"
BoolToString
A bool written out safely.
string BoolToString(
object? input = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
object? |
null |
The value to write: a real bool, a recognised word like "yes" or "off", or a number that is exactly 1 or 0. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "False", when the value is missing or cannot be read as a boolean. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The value reads as a boolean | "True" or "False" — "yes" answers "True" |
"True" or "False" — "yes" answers "True" |
| Unreadable: missing, an unrecognised word, a number other than 1 or 0, or the wrong type | "False" — never empty |
Throws CkTextException — carries Input, Expected and Reason |
Unreadable reads as "False", so a caller cannot tell "absent" from "explicitly no" without the exception.
BoolToString writes a value as "True" or "False" — with capital first letters, the
framework's own spelling. The value is read by CkBool.FromValue, so
the library's one boolean rule applies: a real bool, the words "true", "t", "yes", "y",
"on" and their negatives, or a number that is exactly 1 or 0. It never returns empty.
CkText.BoolToString(true); // -> "True"
CkText.BoolToString("yes"); // -> "True"
CkText.BoolToString(1); // -> "True"
CkText.BoolToString(2); // -> "False" (unreadable — only exactly 1 is true)
CkText.BoolToString("banana"); // -> "False" (unreadable reads as "False")
Writing a boolean for JSON, XML, a URL or a database flag? That lives in CkBool
CkBool.Format writes lowercase "true"/"false" by default —
"True" is not valid JSON, and an XML boolean rejects it — or "1"/"0" and
"Y"/"N" on request, and it answers "" for a value it cannot read instead of a
"False" nobody said.
ToAscii
Plain ASCII, case and spacing kept.
string ToAscii(
string? input = null,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
fallback |
string? |
null |
A spare value, processed the same way. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Every character has an ASCII form | The folded text: "Straße" is "Strasse", "Café" is "Cafe", ligatures and full-width letters their plain letters, "™" is "TM", curly quotes straight, dashes hyphens |
The text |
| A character has none — a Chinese character, an emoji, a currency sign — or nothing remains | "" |
Throws CkTextException — carries Input, Expected and Reason — the reason names the character |
For legacy systems, file names and feeds that cannot carry Unicode. A character with no ASCII form makes the whole value unusable rather than silently dropped, so a name never loses its letters; IsAscii is the check, this the conversion. Markup is stripped and whitespace tidied first, as String does.
CkText.ToAscii("Zoë O'Brien"); // -> "Zoe O'Brien"
CkText.ToAscii("東京"); // -> "" (no ASCII form — rejected, not mangled)
Initials
The letters an avatar shows.
string Initials(
string? input = null,
int max = 2,
string? fallback = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw name. |
max |
int |
2 |
The most initials to keep; below 1 acts as 1. |
fallback |
string? |
null |
A spare value, processed the same way. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when neither the value nor the fallback can be used. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Some word holds a letter | The initials, upper-cased: "John Smith" is "JS", "3M Company" is "MC" |
The initials |
| No word does, or nothing remains | "" |
Throws CkTextException — carries Input, Expected and Reason |
The first letter of each word, at most max of them. A name with more words keeps the
first and the last, so "John Ronald Reuel Tolkien" is "JT" — the shape monograms use.
CkText.Initials("mary-jane o'brien"); // -> "MO"
CkText.Initials("John Ronald Reuel Tolkien", max: 3); // -> "JRT"
Between
The text between two markers.
string Between(
string? input = null,
string? start = null,
string? end = null,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw text, taken as written. |
start |
string? |
null |
The marker the slice begins after; empty for the beginning. |
end |
string? |
null |
The marker the slice ends before, searched after start; empty for the end. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when a marker is not found or nothing lies between them. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| Both markers are found with something between them | The slice, trimmed | The slice |
| A marker is missing, or nothing lies between them | "" |
Throws CkTextException — carries Input, Expected and Reason — the reason says which |
Markers match exactly, case included, on the raw text — so markers made of markup work,
Between(html, "<title>", "</title>"). The slice is otherwise untouched; clean it with
String when it goes further than a comparison.
CkText.Between("Order #1234 - paid", "Order #", " -"); // -> "1234"
CkText.Between("prefix: the rest", "prefix:", ""); // -> "the rest"
Plural
A count with its noun.
string Plural(
object? count = null,
string? singular = null,
string? plural = null,
bool includeCount = true,
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
count |
object? |
null |
The count: a number of any type, or text. |
singular |
string? |
null |
The noun for exactly one. |
plural |
string? |
null |
The noun for any other count; null to form it by the usual English rules. |
includeCount |
bool |
true |
Write the count in front of the noun. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering "", when the count is not a number or the noun is missing. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| The count reads and the noun is given | "1 item", "2 items", "0 items" |
The phrase |
| Rejected: not a number, or no noun | "" |
Throws CkTextException — carries Input, Expected and Reason |
English. The usual rules — "box" to "boxes", "city" to "cities", "day" to "days" — cover most nouns; pass the plural for the ones that break them, "person" and "people".
CkText.Plural(3, "box"); // -> "3 boxes"
CkText.Plural(3, "person", "people"); // -> "3 people"
CkText.Plural(1, "item", includeCount: false); // -> "item"
Ordinal
1st, 2nd, 3rd.
string Ordinal(
object? number = null,
string fallback = "",
bool throwIfInvalid = CkSettings.DefaultThrowIfInvalid
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
number |
object? |
null |
The number: any numeric type, or text. |
fallback |
string |
"" |
What comes back when the value cannot be used. |
throwIfInvalid |
bool |
CkSettings.DefaultThrowIfInvalid (false) |
Pass true to throw a CkTextException that says why, instead of answering the fallback, when the value is not a whole, non-negative number. |
Returns
| When | Default | With throwIfInvalid: true |
|---|---|---|
| A whole, non-negative number | "1st", "22nd", "111th" |
The ordinal |
| Rejected: a fraction, a negative number, junk | The fallback |
Throws CkTextException — carries Input, Expected and Reason |
English, with the teens right: 11th, 12th, 13th.
CkText.Ordinal(22); // -> "22nd"
CkText.Ordinal("3.5"); // -> ""
Explain
Why not — in words, without an exception.
string Explain(
string? input = null
)
| Parameter | Type | Default | What it does |
|---|---|---|---|
input |
string? |
null |
The raw value. |
Returns
| When | You get |
|---|---|
| The value passes the reader | "" |
| It does not | The reason, the same words the exception would carry — an answer, not a failure; it never throws |
The form-friendly twin of throwIfInvalid: the message to show beside the field,
without a try and catch. Every class with a reader has one, on the reader's own
rules, so the reason is exactly what the reader would say.
CkText.Explain("hello"); // -> ""
CkText.Explain("<p></p>"); // -> "nothing left once cleaned"
Good to know
- Accents are composed. A base letter plus a combining mark, the way macOS writes "é", becomes the one composed letter before anything else happens, in every reader of every class: it counts as one character, matches the composed spelling, and is stored that way.
- What leaves is valid text. A .NET string can hold half of a surrogate pair — a value
no encoder, filesystem or normalizer takes. Every method here,
Truncate,BetweenandNullIfEmptyincluded, replaces such a half with U+FFFD, the replacement character a browser would show, so nothing malformed ever comes back. - Markup is stripped the way a browser reads it. A page may write
<and<without the semicolon, and a browser decodes them; so does the strip, before it looks for tags, which is why"<script>…"comes out empty and not as text that turns into a tag on the page it lands on."AT&T"and"fish &chips"begin no such name and stay as they are. A tag holding one lone quote in an attribute (<img title=don't …>) is stripped too. - A mask shows only its shape.
Maskkeeps spaces, punctuation and symbols visible so***-**-6789still reads as a number; a control character, a line break, a direction override or an invisible joiner is masked like a letter, so the mask is always one plain line in a log.ToAsciicleans its folded text once more, since folding a fullwidth<into<can make markup where there was none. Htmlis text extraction, not a security tool. It pulls readable text out of markup; it is not a defence against XSS (cross-site scripting — injected script in a page). Markup is matched with patterns, not truly parsed, so odd input like"a<b and c>d"loses the middle. Encode at render time, and use a real parser for untrusted markup.MaskandTruncatenever cut an emoji in half. Some characters, emoji included, are stored as twochars (a "surrogate pair"); both methods keep the pair together.Join,Split,Mask,Truncate,SameText,Similarity, the validators and the counters take nothrowIfInvalidat all. They transform, compare, judge and count, so an empty, false or zero result is an answer, not a failure.Matchesis the one validator with the flag, and it concerns the pattern — a value that does not match answersfalseeither way.- The validators judge the cleaned value.
Contains,StartsWith,IsDigits,HasLengthand the rest look at the value asStringwould clean it — markup gone, invisible characters removed, whitespace tidied — so their verdict is about what you would store. OnlyWordCountandCharacterCountcan look at the raw value, withstripHtml: false. - Empty is never found.
Contains("abc", "")isfalsehere, unlike the framework'sstring.Contains; an entry that cleans to nothing is skipped byContainsAnyandInList; and an empty value fails every validator exceptIsEmptyandHasLengthwithmin: 0. - Lengths count code points.
HasLengthandCharacterCountcount one per character, so an emoji counts once;maxLengthelsewhere in the class counts UTF-16 units, where an emoji counts twice. ToStringOrNullnever throws. For an OrNull method,nullis the desired answer for anything unusable — there is nothrowIfInvalid. Want errors? Clean with String andthrowIfInvalid: true, then bridge with NullIfEmpty.Digitsdrops meaning along with the separators. "1.5" becomes "15" and "$1,234.56" becomes "123456" — parse real numbers and amounts with CkNumber and CkMoney instead.BoolToStringcannot tell "absent" from "no". Null, an unrecognised word and a non-boolean type all read as "False". Use a nullableboolat the call site if the difference matters — orthrowIfInvalid: true, which lets only real booleans, numbers and recognised words through.- The fallback is cleaned too. A
fallbackgoes through the same cleaning and validation as the input and is never returned as typed; a fallback that fails answers""as well. - Non-empty in, empty out means rejected. That is the standard CommonKit signal —
check for
""where it matters.
If you want errors instead
Pass throwIfInvalid: true and a value that cannot be used raises a
CkTextException (from SafeCoder.CommonKit.Exceptions) instead of returning empty. The
exception carries the Input, what was Expected and a short Reason, flattened into
a one-line message. Join, Split, Mask, Truncate, SameText, Similarity, the
validators and the counters never take the parameter — for them, empty (or false, or 0)
is the answer; Matches takes it for its pattern alone. Neither does
ToStringOrNull — it never throws, because null is its answer for anything unusable;
want errors, clean with String and throwIfInvalid: true first.
CkText.String(
input: "<p></p>",
throwIfInvalid: true
); // throws CkTextException — the message says why