Searching a text
Cleaning, questioning, locating — and the −1 convention, which is not the zero of absences.
A line read from somewhere never shows up the way you hoped: spaces at the edges, a piece of
information to locate before it can be taken. Four methods are enough to put it in order, and
they come from the same StrText 9.1 imported.
import { StrText } from "std/extensions/str-text.arc";
extend archetype str StrText;
@entry(start)
object MyProgram {
start() {
var line := " mandrake : 3 doses ";
var clean := line.trimmed();
var known := clean.startsWith(prefix: "mandrake");
var mark := clean.indexOf(needle: ":", since: 0);
Console.print(text: $"cleaned: “{clean}”");
Console.print(text: $"starts with “mandrake”: {known}");
Console.print(text: $"contains “dose”: {clean.contains(needle: "dose")}");
Console.print(text: $"the colon sits at index {mark}");
Console.print(text: $"the ingredient: {clean.substring(start: 0, end: mark).trimmed()}");
Console.print(text: $"“nightshade”: {clean.indexOf(needle: "nightshade", since: 0)}");
}
}
trimmed returns a copy, it changes nothing
var clean := line.trimmed();
line has not moved: it is still " mandrake : 3 doses ", spaces included. trimmed
returns a text, it does not fix one. That holds for every method in this section, and it
holds for str in general: a text, once written, is not retouched — another one is made. Hence
the assignment: without it, the work is done and then thrown away.
Answering yes or no
clean.startsWith(prefix: "mandrake")
clean.contains(needle: "dose")
Two bools, which drop straight into an if or combine with the and and or of 3.3.
endsWith exists too, the mirror of startsWith.
Locating, and the −1 convention
clean.indexOf(needle: ":", since: 0)
indexOf returns the index of the first occurrence from since onwards — here 9, and the
next one can be looked for by starting again at 10. When the needle is nowhere, the answer is
-1.
Why -1, when the course presented zero as the mark of absence in 5.2? Because zero is
already taken here: it is the index of the very first character, a perfectly valid answer. An
absence cannot be marked by a value that already means something else, and -1 — an index that
does not exist — is free. So keep the shape:
if (clean.indexOf(needle: "nightshade", since: 0) >= 0) {
and not a comparison against zero, which would miss the first character.
That, in fact, is all contains is: indexOf(…) >= 0, written once so it need not be thought
about again.
Locating, then cutting
The last two lines of the program do together what all this is for:
clean.substring(start: 0, end: mark).trimmed()
indexOf gave the index of the colon, substring takes what precedes it, trimmed removes the
space left at the edge — and mandrake remains. The methods chain because each returns a text,
and a text can do everything StrText has taught it.
Over to you
- Print
line.lengththenclean.length: the first has lost nothing. - Look for
":"from10rather than from0: there is no second colon, so-1. - Replace
>= 0with> 0in theifabove and look for"mandrake": the condition is false although the word is right there, at index zero. That is exactly the trap the-1convention asks you to avoid. - Take what follows the colon —
substring(start: mark + 1, end: clean.length)— and clean it up. That piece,3 doses, is the subject of the next subsection.