Standard library · Text

str

Everything to do with text: searching, slicing, replacing, digits, case, and building long strings.

import str
On this page
  1. Types
  2. Functions

The str package is the one you will reach for most. Anywhere you touch text, from reading user input to building output, its functions apply.

One thing to know before you start: Salam strings are UTF-8. In UTF-8 an English letter is one byte, while a letter in most other scripts takes two or more. That is why most of these functions come in two forms: Len, Substr and IndexOf work in bytes and are fast, while CharCount, CharSubstr and CharFind work in characters. Wherever the count or the cut has to match what the reader sees, use the character form.

One step further out is the "grapheme": what a reader sees as a single letter. A letter with an accent is two characters but one grapheme, and a family emoji is seven characters but one grapheme. For reversing or cutting text that carries marks or emoji, the grapheme functions are the safe ones.

Three methods also sit on every string and need no import: text.starts_with("salam"), text.ends_with(".txt") and text.includes("world"). All three return true or false and behave the same on every backend.

Types

StringBuilder

struct StringBuilder
A string builder: the memory in which you build a string piece by piece. Always create one with NewBuilder and use it through the Buf functions; do not change its fields directly.
NameTypeDescription
datavoid*The address of the memory the text is written into.
leni64 = 0How many bytes have been written so far.
capi64 = 0The size of the memory taken, in bytes. It grows by itself when it fills up.
str-StringBuilder.salam
import str

func main:
    mut b := str.NewBuilder()
    defer str.BufFree(b)
    println str.BufIsEmpty(b)
    repeat 1 to 3 in i:
        str.BufAppend(b, "part ")
        str.BufAppendInt(b, i)
        str.BufAppendChar(b, 44)
    end
    str.BufAppendCp(b, 1776)
    println str.BufStr(b)
    println str.BufLen(b), str.BufIsEmpty(b)
    str.BufReset(b)
    println str.BufLen(b)
end
Outputtrue part 1,part 2,part 3,۰ 23 false 0

Functions

Contains

func Contains(s: str, sub: str): bool
True when sub occurs anywhere in the string.
str-Contains.salam
import str

func main:
    s := "salam, salam world"
    println str.Contains(s, "world"), str.StartsWith(s, "salam"), str.EndsWith(s, "world")
    println str.IndexOf(s, "salam"), str.LastIndex(s, "salam"), str.Search(s, "missing")
    println str.IndexFrom(s, "salam", 1), str.Count(s, "salam")
end
Outputtrue true true 0 7 -1 7 2

StartsWith

func StartsWith(s: str, prefix: str): bool
True when the string starts with prefix.

The example for this item is shown with the ones above: str-Contains.salam

EndsWith

func EndsWith(s: str, suffix: str): bool
True when the string ends with suffix.

The example for this item is shown with the ones above: str-Contains.salam

func Search(s: str, sub: str): int
Returns the position of the first occurrence of sub in bytes, or minus one. It is the same as IndexOf.

The example for this item is shown with the ones above: str-Contains.salam

IndexOf

func IndexOf(s: str, sub: str): int
Returns the position of the first occurrence of sub in bytes, or minus one when it is not found.

The example for this item is shown with the ones above: str-Contains.salam

IndexFrom

func IndexFrom(s: str, sub: str, from: int): int
Like IndexOf, but starts the search at byte from, for finding the next occurrence.

The example for this item is shown with the ones above: str-Contains.salam

Count

func Count(s: str, sub: str): int
Returns the number of non-overlapping occurrences of sub in the string.

The example for this item is shown with the ones above: str-Contains.salam

LastIndex

func LastIndex(s: str, sub: str): int
Returns the position of the last occurrence of sub in bytes, or minus one.

The example for this item is shown with the ones above: str-Contains.salam

DigitValue

func DigitValue(cp: int): int
Returns the numeric value of a digit, whether it is written in English, Persian or Arabic digits. For a character that is not a digit it returns minus one.
str-Digits.salam
import str

func main:
    println str.ToPersianDigits("order 1403")
    println str.ToEnglishDigits("code ۱۲۳ and ٤٥٦")
    println str.ToArabicDigits("2025")
    println str.HasPersianDigits("۱۲"), str.HasArabicDigits("۱۲"), str.HasEnglishDigits("12")
    d := str.CodePoints("۷").get(0)
    println str.IsPersianDigit(d), str.IsArabicDigit(d), str.IsAnyDigit(d)
    println str.DigitValue(d), str.DigitValue(65)
end
Outputorder ۱۴۰۳ code 123 and 456 ٢٠٢٥ true false true true false true 7 -1

IsArabicDigit

func IsArabicDigit(cp: int): bool
True when the character is one of the Arabic digits ٠ to ٩. Persian digits have a different shape and do not count here.

The example for this item is shown with the ones above: str-Digits.salam

IsPersianDigit

func IsPersianDigit(cp: int): bool
True when the character is one of the Persian digits ۰ to ۹.

The example for this item is shown with the ones above: str-Digits.salam

IsAnyDigit

func IsAnyDigit(cp: int): bool
True when the character is an English, Persian or Arabic digit. Use this to accept a number from a user with any keyboard.

The example for this item is shown with the ones above: str-Digits.salam

ToEnglishDigits

func ToEnglishDigits(s: str): str
Converts every Persian and Arabic digit in the string to an English one and leaves the rest of the text alone. Useful before storing a phone number or an identifier in a database.

The example for this item is shown with the ones above: str-Digits.salam

ToArabicDigits

func ToArabicDigits(s: str): str
Converts every digit in the string to an Arabic one.

The example for this item is shown with the ones above: str-Digits.salam

ToPersianDigits

func ToPersianDigits(s: str): str
Converts every digit in the string to a Persian one, for showing a number to a Persian-speaking reader.

The example for this item is shown with the ones above: str-Digits.salam

HasEnglishDigits

func HasEnglishDigits(s: str): bool
True when the string contains at least one English digit.

The example for this item is shown with the ones above: str-Digits.salam

HasArabicDigits

func HasArabicDigits(s: str): bool
True when the string contains at least one Arabic digit.

The example for this item is shown with the ones above: str-Digits.salam

HasPersianDigits

func HasPersianDigits(s: str): bool
True when the string contains at least one Persian digit.

The example for this item is shown with the ones above: str-Digits.salam

GraphemeCount

func GraphemeCount(s: str): int
Returns the number of grapheme clusters, that is, the number of "letters" a reader sees.
str-Graphemes.salam
import str

func main:
    s := "کَتاب👨‍👩‍👧"
    println str.CharCount(s), str.GraphemeCount(s)
    println str.Graphemes(s)
    println str.ReverseGraphemes("سَلام")
end
Output10 5 ["کَ", "ت", "ا", "ب", "👨‍👩‍👧"] مالسَ

Graphemes

func Graphemes(s: str): Vector<str>
Splits the string into grapheme clusters: a letter with its marks, or a composed emoji, is one piece.

The example for this item is shown with the ones above: str-Graphemes.salam

ReverseGraphemes

func ReverseGraphemes(s: str): str
Reverses the string cluster by cluster, so that marks stay attached to their letter.

The example for this item is shown with the ones above: str-Graphemes.salam

NFD

func NFD(s: str): str
Converts the string to Unicode "canonical decomposition": every composed character is split into its base and its marks.
str-NFC.salam
import str

func main:
    composed := "آ"
    decomposed := str.NFD(composed)
    println composed.len(), decomposed.len(), composed == decomposed
    println str.NFC(decomposed) == composed
    println str.IsNFC(composed), str.IsNFC(decomposed)
    presentation := "ﻼ"
    println str.NFKD(presentation), str.NFKC(presentation)
end
Output2 4 false true true false لا لا

NFC

func NFC(s: str): str
Converts the string to "canonical composition": bases and marks are recombined into single characters wherever possible. Two strings that look identical but are stored with different codes become equal after this, so apply it before comparing or storing.

The example for this item is shown with the ones above: str-NFC.salam

NFKD

func NFKD(s: str): str
Like canonical decomposition, but it also expands the "compatibility" forms; Arabic presentation forms such as ﻻ, for instance, become ordinary letters.

The example for this item is shown with the ones above: str-NFC.salam

NFKC

func NFKC(s: str): str
Compatibility decomposition followed by canonical composition. It suits text gathered from mixed sources, such as PDFs.

The example for this item is shown with the ones above: str-NFC.salam

IsNFC

func IsNFC(s: str): bool
True when the string is already in canonical composition. The check is cheaper than the conversion.

The example for this item is shown with the ones above: str-NFC.salam

Upper

func Upper(s: str): str
Converts letters to upper case. Scripts without letter case, such as Persian, are left alone.
str-Upper.salam
import str

func main:
    println str.Upper("salam"), str.Lower("SALAM"), str.Title("hello world")
    println str.EqualFold("Straße", "STRASSE")
    println str.CompareFold("abc", "ABX")
    println str.Upper("سلام")
end
OutputSALAM salam Hello World true -21 سلام

Lower

func Lower(s: str): str
Converts letters to lower case.

The example for this item is shown with the ones above: str-Upper.salam

Title

func Title(s: str): str
Capitalises the first letter of every word.

The example for this item is shown with the ones above: str-Upper.salam

EqualFold

func EqualFold(a: str, b: str): bool
Compares two strings ignoring letter case, following the full Unicode rules, so that "Straße" equals "STRASSE".

The example for this item is shown with the ones above: str-Upper.salam

CompareFold

func CompareFold(a: str, b: str): int
Compares two strings ignoring letter case and returns a negative number, zero or a positive one; only the sign matters, not the size.

The example for this item is shown with the ones above: str-Upper.salam

CodePointAt

func CodePointAt(s: str, i: int, cp &: int): int
Reads the character starting at byte i, puts its code point in cp and returns how many bytes it took. For walking a string by hand, character by character.
str-CharCount.salam
import str

func main:
    s := "سلام"
    println str.Len(s), str.CharCount(s)
    println str.CharAtIndex(s, 1), str.CharSubstr(s, 1, 2)
    println str.CharFind(s, "ام"), str.Reverse(s)
    println str.CharAt(s, 2)
    mut code := 0
    width := str.CodePointAt(s, 0, code)
    println code, width, str.CharLen(216)
    points := str.CodePoints(s)
    println points
    println str.FromCodePoints(points)
    println str.Chars("ab۱")
    println str.AllAscii("salam"), str.AllAscii("سلام")
end
Output8 4 ل لا 2 مالس ل 1587 2 2 [1587, 1604, 1575, 1605] سلام ["a", "b", "۱"] true false

CharLen

func CharLen(lead: int): int
From the first byte of a character, says how many bytes that character takes, from 1 to 4.

The example for this item is shown with the ones above: str-CharCount.salam

CharAt

func CharAt(s: str, i: int): str
Returns the character starting at byte i. That is a byte position, not a character position; for a character position see CharAtIndex.

The example for this item is shown with the ones above: str-CharCount.salam

Reverse

func Reverse(s: str): str
Reverses the order of the characters in the string. If the text carries marks or emoji, ReverseGraphemes is the correct one.

The example for this item is shown with the ones above: str-CharCount.salam

CharCount

func CharCount(s: str): int
Returns the number of characters in the string rather than bytes. For "سلام" it is four, while Len says eight.

The example for this item is shown with the ones above: str-CharCount.salam

CharSubstr

func CharSubstr(s: str, start: int, n: int): str
Cuts n characters starting at character start. It is the safe version of Substr for text outside ASCII.

The example for this item is shown with the ones above: str-CharCount.salam

CharAtIndex

func CharAtIndex(s: str, i: int): str
Returns character number i, counting from zero.

The example for this item is shown with the ones above: str-CharCount.salam

CharFind

func CharFind(s: str, sub: str): int
Returns the position of the first occurrence of sub in characters, or minus one when it is not there.

The example for this item is shown with the ones above: str-CharCount.salam

CodePoints

func CodePoints(s: str): Vector<int>
Returns the code points of every character in the string, in a vector.

The example for this item is shown with the ones above: str-CharCount.salam

FromCodePoints

func FromCodePoints(cps: Vector<int>): str
The reverse of CodePoints: builds a string from a vector of code points.

The example for this item is shown with the ones above: str-CharCount.salam

AllAscii

func AllAscii(s: str): bool
True when every character in the string is ASCII. Useful for choosing a faster path when processing text.

The example for this item is shown with the ones above: str-CharCount.salam

Chars

func Chars(s: str): Vector<str>
Returns each character of the string as a separate string in a vector.

The example for this item is shown with the ones above: str-CharCount.salam

Repeat

func Repeat(s: str, n: int): str
Repeats the string n times, one after another; good for drawing a separator line.
str-Fields.salam
import str

func main:
    println str.Repeat("=-", 5)
    words := str.Fields("  the Salam   programming\tlanguage \n")
    println words.len(), words
end
Output=-=-=-=-=- 4 ["the", "Salam", "programming", "language"]

Fields

func Fields(s: str): Vector<str>
Splits the string into words on any run of spaces, tabs or newlines, dropping the empty pieces.

The example for this item is shown with the ones above: str-Fields.salam

NewBuilder

func NewBuilder(): StringBuilder
Creates an empty string builder. When you are building a long piece of text in parts, this is far faster than joining with + over and over. Release it with BufFree when you are done.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufAppend

func BufAppend(b &: StringBuilder, s: str)
Adds a string to the end of the builder.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufAppendChar

func BufAppendChar(b &: StringBuilder, code: int)
Adds one byte by its numeric code, for ASCII characters such as a comma (44).

The example for this item is shown with the ones above: str-StringBuilder.salam

BufAppendCp

func BufAppendCp(b &: StringBuilder, cp: int)
Adds one Unicode character by its code point, for anything outside ASCII.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufAppendInt

func BufAppendInt(b &: StringBuilder, n: i64)
Adds a whole number in its text form.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufLen

func BufLen(b &: StringBuilder): i64
Returns the size of the text built so far, in bytes.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufIsEmpty

func BufIsEmpty(b &: StringBuilder): bool
True when nothing has been added to the builder yet.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufStr

func BufStr(b &: StringBuilder): str
Returns the text built so far as a fresh string. The builder stays usable afterwards.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufReset

func BufReset(b &: StringBuilder)
Empties the builder but keeps its memory, so it can be reused for the next piece of text.

The example for this item is shown with the ones above: str-StringBuilder.salam

BufFree

func BufFree(b &: StringBuilder)
Releases the builder's memory. The best place for it is a defer right after creating the builder.

The example for this item is shown with the ones above: str-StringBuilder.salam

Len

func Len(s: str): int
Returns the length of the string in bytes. For the number of characters see CharCount.
str-Len.salam
import str

func main:
    a := "apple"
    b := "pear"
    println str.Len(a), str.IsEmpty(""), str.IsEmpty(a)
    println str.Concat(a, b)
    println str.Compare(a, b), str.Compare(b, a), str.Compare(a, "apple")
    println str.Equals(a, "apple")
    println str.Substr("salam donya", 6, 5), str.SubstrIn("salam", 3, 10)
    println str.Find("salam donya", "donya")
    c := str.Clone(a)
    println c
end
Output5 true false applepear -1 1 0 true donya am 6 apple

ByteLen

func ByteLen(s: str): int
Returns the length of the string in bytes and gives the same answer on every backend. On the JavaScript backend Len counts UTF-16 units, so use this one for byte-level work.
str-ByteLen.salam
import str

func main:
    s := "سلام"
    println str.ByteLen(s), str.CharCount(s)
    println str.ByteSub(s, 0, 4)
    println str.CharSubstr(s, 0, 2)
end
Output8 4 سل سل

ByteSub

func ByteSub(s: str, start: int, n: int): str
Cuts n bytes starting at byte start. The C and LLVM backends return the bytes untouched, but the JavaScript backend decodes them as UTF-8, so a cut that falls inside a multi-byte character produces a replacement character and a different byte length. For cutting by character use CharSubstr.

The example for this item is shown with the ones above: str-ByteLen.salam

Concat

func Concat(a: str, b: str): str
Joins two strings one after the other; the same thing + does.

The example for this item is shown with the ones above: str-Len.salam

Compare

func Compare(a: str, b: str): int
Compares two strings byte by byte: minus one when the first comes earlier, zero when they are equal and one when it comes later. It is what you want for sorting strings.

The example for this item is shown with the ones above: str-Len.salam

Equals

func Equals(a: str, b: str): bool
True when the two strings are exactly the same; the same thing == does.

The example for this item is shown with the ones above: str-Len.salam

Substr

func Substr(s: str, start: int, n: int): str
Cuts n bytes starting at byte start. Outside ASCII, take care not to cut through the middle of a character, or use CharSubstr instead.

The example for this item is shown with the ones above: str-Len.salam

SubstrIn

func SubstrIn(s: str, start: int, n: int): str
Like Substr, but when the range runs past the end of the string it does not fail and returns only the part that is there.

The example for this item is shown with the ones above: str-Len.salam

Find

func Find(s: str, sub: str): int
Returns the position of the first occurrence of sub in bytes, or minus one.

The example for this item is shown with the ones above: str-Len.salam

Split

func Split(s: str, delim: str): Vector<str>
Splits the string wherever delim occurs and returns the pieces in a vector. Free the vector when you are done with it.
str-Split.salam
import str

func main:
    parts := str.Split("apple,pear,grape", ",")
    defer parts.free()
    println parts.len(), parts.get(1)
    println str.Join(parts, " / ")
end
Output3 pear apple / pear / grape

Join

func Join(parts: Vector<str>, sep: str): str
Joins the pieces of a vector with sep between them; the reverse of Split.

The example for this item is shown with the ones above: str-Split.salam

Trim

func Trim(s: str): str
Removes the spaces at both ends. Use it on user input before anything else.
str-Trim.salam
import str

func main:
    s := "   hello world   "
    println "[" + str.Trim(s) + "]"
    println "[" + str.TrimLeft(s) + "]"
    println "[" + str.TrimRight(s) + "]"
    println str.TrimPrefix("https://salamlang.ir", "https://")
    println str.TrimSuffix("report.txt", ".txt")
end
Output[hello world] [hello world ] [ hello world] salamlang.ir report

ToInt

func ToInt(s: str): int
Turns the string into a whole number, accepting Persian and Arabic digits too. When it finds no number it returns zero.
str-ToInt.salam
import str

func main:
    println str.ToInt("42") + 1, str.ToInt("abc")
    println str.ToFloat("2.5") * 2
    println "[" + str.FromInt(-15) + "]", "[" + str.FromFloat(1.25) + "]"
end
Output43 0 5 [-15] [1.25]

ToFloat

func ToFloat(s: str): f64
Turns the string into a decimal number.

The example for this item is shown with the ones above: str-ToInt.salam

IsEmpty

func IsEmpty(s: str): bool
True when the string has no characters at all.

The example for this item is shown with the ones above: str-Len.salam

Clone

func Clone(s: str): str
Makes an independent copy of the string.

The example for this item is shown with the ones above: str-Len.salam

Replace

func Replace(s: str, find: str, replacement: str): str
Replaces every occurrence of find with replacement.
str-Replace.salam
import str

func main:
    println str.Replace("I like apples, red apples", "apple", "pear")
end
OutputI like pears, red pears

FromInt

func FromInt(i: i64): str
Turns a whole number into text.

The example for this item is shown with the ones above: str-ToInt.salam

FromFloat

func FromFloat(f: f64): str
Turns a decimal number into text.

The example for this item is shown with the ones above: str-ToInt.salam

IsAscii

func IsAscii(code: int): bool
True when the code is in the ASCII range, zero to 127.
str-IsAscii.salam
import str

func main:
    each s in ["A", "z", "7", " ", "!", "ب", "۷", "\t"]:
        c := str.CodePoints(s).get(0)
        println "[" + s + "]", str.IsAscii(c), str.IsAlpha(c), str.IsDigit(c), str.IsSpace(c), str.IsPunct(c)
    end
    println str.IsAsciiUpper(65), str.IsAsciiLower(65), str.IsUpper(1040), str.IsLower(1072)
    println str.IsAsciiDigit(50), str.IsAsciiAlpha(50), str.IsAsciiAlnum(50), str.IsAsciiSpace(32)
    println str.IsAlnum(1776), str.IsMark(1614), str.IsControl(10)
end
Output[A] true true false false false [z] true true false false false [7] true false true false false [ ] true false false true false [!] true false false false true [ب] false true false false false [۷] false false true false false [ ] true false false true false true false true true true false true true true true true

IsAsciiSpace

func IsAsciiSpace(code: int): bool
Only ASCII whitespace: space, tab, newline and the like.

The example for this item is shown with the ones above: str-IsAscii.salam

IsAsciiDigit

func IsAsciiDigit(code: int): bool
Only the English digits 0 to 9.

The example for this item is shown with the ones above: str-IsAscii.salam

IsAsciiUpper

func IsAsciiUpper(code: int): bool
Only the capital English letters A to Z.

The example for this item is shown with the ones above: str-IsAscii.salam

IsAsciiLower

func IsAsciiLower(code: int): bool
Only the small English letters a to z.

The example for this item is shown with the ones above: str-IsAscii.salam

IsAsciiAlpha

func IsAsciiAlpha(code: int): bool
Only English letters, capital or small.

The example for this item is shown with the ones above: str-IsAscii.salam

IsAsciiAlnum

func IsAsciiAlnum(code: int): bool
An English letter or digit.

The example for this item is shown with the ones above: str-IsAscii.salam

IsSpace

func IsSpace(code: int): bool
Any Unicode whitespace, including the ASCII spaces and the ideographic space.

The example for this item is shown with the ones above: str-IsAscii.salam

IsDigit

func IsDigit(code: int): bool
Any Unicode decimal digit, Persian and Arabic included.

The example for this item is shown with the ones above: str-IsAscii.salam

IsUpper

func IsUpper(code: int): bool
A capital letter in any script that has them, such as Latin, Greek and Cyrillic.

The example for this item is shown with the ones above: str-IsAscii.salam

IsLower

func IsLower(code: int): bool
A small letter in any script that has them.

The example for this item is shown with the ones above: str-IsAscii.salam

IsAlpha

func IsAlpha(code: int): bool
Any letter in any script.

The example for this item is shown with the ones above: str-IsAscii.salam

IsAlnum

func IsAlnum(code: int): bool
Any letter or digit in any script.

The example for this item is shown with the ones above: str-IsAscii.salam

IsPunct

func IsPunct(code: int): bool
Any punctuation mark, such as a full stop, a comma or a question mark.

The example for this item is shown with the ones above: str-IsAscii.salam

IsMark

func IsMark(code: int): bool
A combining mark, such as an accent or an Arabic vowel sign.

The example for this item is shown with the ones above: str-IsAscii.salam

IsControl

func IsControl(code: int): bool
A control character such as a newline or a tab: invisible, and doing something rather than showing something.

The example for this item is shown with the ones above: str-IsAscii.salam

TrimLeft

func TrimLeft(s: str): str
Removes the spaces, tabs and newlines at the start of the string.

The example for this item is shown with the ones above: str-Trim.salam

TrimRight

func TrimRight(s: str): str
Removes the spaces, tabs and newlines at the end of the string.

The example for this item is shown with the ones above: str-Trim.salam

TrimPrefix

func TrimPrefix(s: str, prefix: str): str
If the string starts with prefix, removes it; otherwise returns the string untouched.

The example for this item is shown with the ones above: str-Trim.salam

TrimSuffix

func TrimSuffix(s: str, suffix: str): str
If the string ends with suffix, removes it; handy for stripping a file extension.

The example for this item is shown with the ones above: str-Trim.salam