Skip to content

Strings and bytes#

Strings are sequences of Unicode code points. Bytes represent raw binary data.

String literals#

Use double quotes or single quotes for standard strings.

"hello"
// result: "hello" (string)
'hello'
// result: "hello" (string)

Use triple-quotes to create multi-line strings.

"""multi
line"""
// result: "multi\nline" (string)

Raw strings use the r prefix and preserve content literally.

r"no\nescape"
// result: "no\\nescape" (string)

Escape sequences#

Standard strings interpret escape sequences. Raw strings do not.

Sequence Description
\\ Backslash
\' Single quote
\" Double quote
\` Backtick
\a Bell
\b Backspace
\f Form feed
\n Newline
\r Carriage return
\t Tab
\v Vertical tab
\xHH Hex byte (2 digits)
\000 Octal byte (3 digits)
\uHHHH Unicode code point (BMP)
\UHHHHHHHH Unicode code point (any plane)
"line1\nline2"
// result: "line1\nline2" (string)
"\u0041"
// result: "A" (string)

Byte literals#

Use the b prefix with a string literal.

b"hello"
// result: b"hello" (bytes)

Bytes support the same escape sequences as strings, except for Unicode escapes (\u and \U).

b"\x00\xff"
// result: b"\x00\xff" (bytes)

Size#

size() returns the length. For strings, it counts code points. For bytes, it counts bytes.

"hello".size()
// result: 5 (int)
b"hello".size()
// result: 5 (int)

A simple emoji is one code point but multiple bytes.

"👋".size()
// result: 1 (int)
b"👋".size()
// result: 4 (int)

Concatenation#

+ joins strings or bytes together.

"hello" + " " + "world"
// result: "hello world" (string)
b"\x00" + b"\x01"
// result: b"\x00\x01" (bytes)

contains() checks if a string includes a substring. Matching is case-sensitive.

"hello world".contains("world")
// result: true (bool)
"hello world".contains("World")
// result: false (bool)

startsWith() and endsWith() check string boundaries.

"hello".startsWith("he")
// result: true (bool)
"hello".endsWith("lo")
// result: true (bool)

Regular expressions#

matches() tests if a regular expression matches anywhere in the string. CEL uses RE2 syntax for regular expressions.

"hello".matches("h.*o")
// result: true (bool)
"hello world".matches("world")
// result: true (bool)

Use anchors to match the entire string.

"hello world".matches("^hello$")
// result: false (bool)
"hello".matches("^hello$")
// result: true (bool)

Conversion#

Convert between strings and bytes.

bytes("hello")
// result: b"hello" (bytes)
string(b"hello")
// result: "hello" (string)

Converting bytes to string requires valid UTF-8.

string(b"\xff\xfe")
// error: invalid UTF-8 in bytes, cannot convert to string

Convert other types to strings.

string(123)
// result: "123" (string)
string(3.14)
// result: "3.14" (string)
string(true)
// result: "true" (string)

Convert strings to other types.

int("42")
// result: 42 (int)
double("3.14")
// result: 3.14 (double)

Strings extension#

The CEL strings extension adds string manipulation functions. Extensions must be enabled in the CEL runtime, which may not be supported.

Case conversion#

lowerAscii() converts ASCII characters to lowercase. Non-ASCII characters are unchanged.

"Hello, World!".lowerAscii()
// result: "hello, world!" (string)
"CAFÉ".lowerAscii()
// result: "cafÉ" (string)

upperAscii() converts ASCII characters to uppercase. Non-ASCII characters are unchanged.

"Hello, World!".upperAscii()
// result: "HELLO, WORLD!" (string)

indexOf() returns the position of the first occurrence of a substring. Returns -1 if not found. An optional second argument specifies the position to start searching from.

"hello world".indexOf("world")
// result: 6 (int)
"hello world".indexOf("missing")
// result: -1 (int)
"abcabc".indexOf("abc", 1)
// result: 3 (int)

lastIndexOf() returns the position of the last occurrence of a substring. Returns -1 if not found. An optional second argument specifies the last position to consider as the start of a match.

"abcabc".lastIndexOf("abc")
// result: 3 (int)
"abcabc".lastIndexOf("abc", 2)
// result: 0 (int)

Character access#

charAt() returns the character at the given position in the string. The position must be between zero and the string length, inclusive. When the position equals the string length, an empty string is returned.

"hello".charAt(1)
// result: "e" (string)
"hello".charAt(5)
// result: "" (string)
"hello".charAt(6)
// error: index out of range: 6

Substring#

substring() returns a portion of the string from a start index. An optional second argument specifies the end index (exclusive).

"hello world".substring(6)
// result: "world" (string)
"hello world".substring(0, 5)
// result: "hello" (string)

Replace#

replace() replaces occurrences of a search string with a replacement. An optional third argument limits the number of replacements.

"hello world".replace("l", "x")
// result: "hexxo worxd" (string)
"hello world".replace("l", "x", 1)
// result: "hexlo world" (string)

Split and join#

split() divides a string into a list of substrings by a separator. An optional second argument limits the number of splits.

"a,b,c".split(",")
// result: ["a", "b", "c"] (list)
"a,b,c".split(",", 2)
// result: ["a", "b,c"] (list)

join() is a method on string lists that concatenates elements into a single string. An optional argument specifies a separator.

["a", "b", "c"].join()
// result: "abc" (string)
["a", "b", "c"].join("-")
// result: "a-b-c" (string)

Trim#

trim() removes leading and trailing whitespace using the Unicode definition of whitespace.

"  hello  ".trim()
// result: "hello" (string)
"\t\nhello\n\t".trim()
// result: "hello" (string)

Reverse#

reverse() returns a new string with the code points in reverse order.

"hello".reverse()
// result: "olleh" (string)

Quote#

strings.quote() is a global function (not a method) that escapes a string so it is safe to print. Invalid UTF-8 characters are replaced with \uFFFD.

strings.quote("hello\tworld")
// result: "\"hello\\tworld\"" (string)

Format#

format() provides printf-style string formatting. It takes a single list argument containing the values to substitute. The function is not variadic.

Verb Description Precision
%s String representation No
%d Decimal integer No
%f Floating-point Yes (default 6)
%e Scientific notation Yes (default 6)
%x Hexadecimal (lowercase) No
%X Hexadecimal (uppercase) No
%o Octal No
%b Binary No
"Hello, %s!".format(["world"])
// result: "Hello, world!" (string)
"%s is %d years old".format(["Alice", 30])
// result: "Alice is 30 years old" (string)
"Hex: %x".format([255])
// result: "Hex: ff" (string)

Use .N to specify precision.

"%.2f".format([3.14159])
// result: "3.14" (string)
"%.2e".format([6.02214076e23])
// result: "6.02e+23" (string)

Format errors#

Passing a non-list argument produces a compile error.

"%s".format("world")
// error: found no matching overload for 'format' applied to 'string.(string)'

Providing fewer arguments than format verbs produces an error.

"%s %s".format(["world"])
// error: index 1 out of range

Using the wrong type for a format verb produces an error.

"%d".format(["hello"])
// error: decimal clause can only be used on ints, uints, and doubles, was given string

See also#