Summary#
These are the read-only questions about a string. Text.Length(s) is its length; Text.IsEmpty(s) and
Text.IsBlank(s) test for emptiness; and Text.Contains(s, part), Text.StartsWith(s, part),
Text.EndsWith(s, part) test a string against a literal piece of text. The membership checks are
ordinal — case-sensitive, and the argument is plain text, not a pattern.
Signature#
Text.Length(<string> s) -> int
Text.IsEmpty(<string> s) -> bool // length is 0
Text.IsBlank(<string> s) -> bool // empty, or only whitespace
Text.Contains(<string> s, <string> part) -> bool
Text.StartsWith(<string> s, <string> part) -> bool
Text.EndsWith(<string> s, <string> part) -> boolDescription#
Length is UTF-16 code units, not visible characters#
Text.Length counts UTF-16 code units (the C# string.Length), so an astral character outside the
Basic Multilingual Plane — an emoji, for instance — counts as two. Text.Length("🎉") is 2, not 1.
For plain text this is the character count you expect; the distinction only shows up on emoji and other
astral symbols.
Empty vs blank#
Text.IsEmpty(s) is true only for the zero-length string "". Text.IsBlank(s) is broader: it is true
for "" and for a string that is entirely whitespace, using the full Unicode whitespace set — so an
ideographic space ( ) or a no-break space ( ) counts as blank too, not just an ASCII space.
Contains / StartsWith / EndsWith are ORDINAL and take a literal#
The match is case-sensitive: Text.Contains("Hello World", "world") is false. And the argument is a
literal run of text — a % or _ in it is just that character, with no wildcard meaning. An empty
part is contained by everything: Text.Contains(s, "") is always true.
Text.Contains(s, part)and the member forms.Contains(part)are the same call. The member spelling (Contains, StartsWith, EndsWith) is just instance sugar — same ordinal, case-sensitive, literal semantics. Write whichever reads better. Both push into a query where the receiver is a column. For pattern matching on an in-hand string use Regex — but only in memory; it has no query-predicate form.
All six run wherever they are needed — computed on a string already in hand, and (being pure) they run in the browser with no round trip where a UI action needs them, or push into a query when the receiver is a column (execution side).
Examples#
// Reject a blank required field, and flag anything past a length budget.
string CheckName(string name) {
if (Text.IsBlank(name)) { return "required"; }
if (Text.Length(name) > 40) { return "too long"; }
return "ok";
}[Test]
void Text_inspection_answers() {
Assert.Equal("required", CheckName(" ")); // whitespace-only fails the blank check
Assert.Equal("ok", CheckName("Ada Lovelace"));
Assert.Equal(5, Text.Length("hello"));
Assert.Equal(2, Text.Length("🎉")); // UTF-16 code units — the emoji counts twice
Assert.True(Text.IsEmpty(""));
Assert.False(Text.IsEmpty(" ")); // a space is not empty…
Assert.True(Text.IsBlank(" ")); // …but it is blank
Assert.False(Text.IsBlank(" hi "));
Assert.True(Text.Contains("Hello World", "World"));
Assert.False(Text.Contains("Hello World", "world")); // ordinal → case-sensitive
Assert.True(Text.Contains("Hello", "")); // everything contains the empty string
Assert.True(Text.StartsWith("Hello", "He"));
Assert.False(Text.StartsWith("Hello", "he"));
Assert.True(Text.EndsWith("Hello", "lo"));
}See also#
- Contains, StartsWith, EndsWith — the member-call spelling
s.Contains(x), the same ordinal-literal test as this - Regex — pattern matching, when a literal check is not enough
- Text.Split · Text.TitleCase — the other in-memory string builtins