Strings and String Manipulation

A string is a sequence of characters. In Jai, a string is represented as an array view of bytes (u8). Unlike the C programming language, Jai strings are NOT zero-terminated. The strings in Jai are UTF-8, and do not support unicode.

Here is a basic struct definition of a string:

string :: struct {
  count: int; // amount of characters in the string
  data: *u8;  // pointer to the start of the characters
}

Strings as boolean values

Strings implicitly cast to boolean values that can be then used in if statements. if str is true, then str has charaters in it. If str is false, str is the empty string.

str : string = "Hello.";
if str {

} else {

}

Multi-line String

Multi-line strings can be declared first by typing in the identifier, followed by an assign statement, #string, following by a token indicator for the end of the string, followed by the multi-line string.

multi_line_string := #string END_STRING
This
is
a
multi-line
string.
END_STRING

print(multi_line_string);

In the example above, multi_line_string prints out:

This
is
a
multi-line
string.

String operations

Here are some common string operations people may use that are available in the String and Basic module:

// string comparing functions
equal :: (a: string, b: string) -> bool;
compare :: (a: string, b: string) -> int;
contains :: (str: string, substring: string) -> bool;

(read about what #must does)

equal checks if two strings are equal. Returns true if both strings are equal. Returns false if not equal. compare compares two strings a and b. Returns -1 if a is less than b, 1 if a is greater than b, and 0 if they are equal. Similar to strcmp in C. contains returns true if the string contains substring.


// string begins with and ends with functions
begins_with :: (str: string, prefix: string) -> bool;
ends_with :: (str: string, suffix: string) -> bool;

begins_with checks if a given string begins with a specified prefix. ends_with checks if a given string ends with a specified suffix.


// concatenating and spliting strings
join :: (inputs: string, separator := "", before_first := false, after_last := false) -> string;
split :: (str: string, separator: string) -> [] string;

Joins all the string inputs together to form a larger string. For example: join("a","b","c","d") outputs “abcd”. split splits an input string according to a separator.


// string to int/float and parsing functions
string_to_int :: (str: string) -> int, bool;
string_to_float :: (str: string) -> float, bool;
parse_float :: (line: *string) -> float, bool;
parse_int :: (line: *string) -> int, bool;
parse_token :: (line: *string) -> string, bool;

Attempts to parse a string into a float, token, or int.


sprint :: (fmt: string, args: ..Any) -> string;

// prints out "Congratulations John! You won with a score of: 1000"
name := "John";
score := 1000;
str := sprint("Congratulations %! You won with a score of: %\n", name, score);

sprint functions similar to print, except instead of printing directly to the console, it outputs a string as a return value.


// c string manipulation functions
to_c_string :: (str: string) -> *u8; // NOTE: This function heap allocates memory
c_style_strlen :: (ptr: *u8) -> int;

These functions manipulate c-strings received from c-libraries.

String Builder

init_string_builder :: (builder: *String_Builder, buffer_size := -1)

This function initializes the String Builder .

builder: String_Builder;
builder.allocator = temp;

This line of code sets the String_Builder's allocator to whatever context allocator one wants. In this case, we set it to the temporary allocator.

free_buffers :: (builder: *String_Builder)

This function deallocates the String Builder memory.

append :: (builder: *String_Builder, s: *u8, length: s64)
append :: (builder: *String_Builder, s: string)
append :: (builder: *String_Builder, byte: u8)

Appends a string to the buffer. Used to concatenate multiple strings together, for example, when in a loop.

print_to_builder :: (builder: *String_Builder, format_string: string, args: ..Any) -> bool

Prints out the items to the String_Builder. Has a format similar to the print function. Here is an example use case:

builder: String_Builder;

number := 42;
print_to_builder(*builder, "My name is: %1 %2. My favorite number is: %3\n", "Issac", "Newton", number);
print("String_Builder output is: [%]\n", builder_to_string(*builder));
builder_to_string :: (builder: *String_Builder, allocator := context.allocator, extra_bytes_to_prepend := 0) -> string

Takes the String_Builder contents and returns a string.