Skip to content

Working with text#

This page covers everyday string tasks with AString, AStringView and AStringVector.

AString is a UTF-8 string derived from std::string, so it works wherever std::string does. Positions and sizes are measured in bytes, as in std::string, while text-aware operations (case conversion, trimming by a set of symbols, conversion to other encodings) understand Unicode. When you need to count in characters, use utf8().

Creating strings#

A string literal converts to AString implicitly. The _as suffix makes a literal an AString right away, which is handy for chaining calls:

AString hello = "Привет, мир";
auto length = "hello"_as.length();

If your data is in some other encoding, use AString::fromUtf8, fromUtf16, fromUtf32 or fromLatin1. To get the data back, use toUtf16(), toUtf32() or encode().

1
2
3
AString str("Привет, 🤡, Как твои дела?");
EXPECT_EQ(str.toUtf16(), u"Привет, 🤡, Как твои дела?");
EXPECT_EQ(str.toUtf32(), U"Привет, 🤡, Как твои дела?");

Characters vs bytes#

length(), substr(), erase() and the like operate on bytes, because that's what std::string does. A single character such as 嗨 takes three bytes in UTF-8. To work with characters, switch to the UTF-8 view with utf8(). It provides length(), substr(), iterators and other operations in terms of Unicode code points. See AUtf8View.

1
2
3
4
EXPECT_EQ("嗨"_as, "嗨");
EXPECT_EQ("嗨"_as.length(), 3);
EXPECT_EQ("嗨"_as.utf8().length(), 1);
EXPECT_EQ("嗨"_as.toStdString(), "嗨");
1
2
3
4
AString str("🤡, как твои дела?");
EXPECT_EQ(str.utf8().substr(0, 1), "🤡");
EXPECT_EQ(str.utf8().substr(1), ", как твои дела?");
EXPECT_EQ(str.utf8().substr(1, 3), ", к");

insert() and erase() on AString take byte positions, but insert() also accepts an AChar, which gets encoded properly. Mind that the position must not point into the middle of a multibyte character:

1
2
3
4
auto s = "AB"_as;
s.insert(1, U'🤡');
EXPECT_EQ(s, "A🤡B");
EXPECT_EQ(s.bytes().size(), 6);

Converting to and from numbers#

Parsing never throws. toInt(), toLong(), toUInt(), toFloat(), toDouble() and friends return an AOptional that is empty if the whole string is not a valid number or the value doesn't fit. Hexadecimal literals starting with 0x are understood too.

EXPECT_EQ("123"_as.toInt(), 123);
EXPECT_EQ("0"_as.toInt(), 0);
EXPECT_EQ("-251"_as.toInt(), -251);
EXPECT_EQ("123abs"_as.toInt(), std::nullopt);
EXPECT_EQ("1a23"_as.toInt(), std::nullopt);
EXPECT_EQ("a123"_as.toInt(), std::nullopt);
EXPECT_EQ("0x"_as.toInt(), 0);
EXPECT_EQ("0x123"_as.toInt(), 0x123);
EXPECT_EQ("0xabcdef"_as.toInt(), 0xabcdef);
EXPECT_EQ("0xabcdef123456"_as.toInt(), std::nullopt); // overflow
EXPECT_EQ("123456214214"_as.toInt(), std::nullopt); // overflow

If you prefer exceptions, use the ...OrException variants (toIntOrException(), ...). Use AString::number() to format a number as a string.

Formatting#

AString uses the fmt library. Call format() on a string or use the _format literal:

EXPECT_EQ("test {}"_format("azaza"), "test azaza");

Format specifiers work as in fmt:

EXPECT_EQ("test {:.2}"_format(1.2f), "test 1.2");

_format is declared in AUI/Traits/strings.h. Standard containers and AString itself can be passed as arguments.

Searching, replacing and trimming#

1
2
3
4
5
EXPECT_EQ("Ку🤡Ку"_as.utf8().startsWith(U'К'), true);
EXPECT_EQ("Ку🤡Ку"_as.startsWith("Ку"), true); // russian letters

EXPECT_EQ("Ку🤡Ку"_as.utf8().startsWith(U'K'), false);
EXPECT_EQ("Ку🤡Ку"_as.startsWith("Ky"), false); // english letters

replaceAll() and removeAll() modify the string in place, while replacedAll() and removedAll() return a modified copy.

EXPECT_EQ("abcdef"_as.replaceAll("ab", "12"), "12cdef");

uppercase() and lowercase() are Unicode-aware:

EXPECT_EQ("a"_as.uppercase(), "A"); // ascii
EXPECT_EQ("å"_au8.uppercase(), "Å"); // unicode

trim(), trimLeft() and trimRight() remove a single symbol (a space by default)...

1
2
3
4
EXPECT_EQ(AString("  a b  ").trim(), "a b");
EXPECT_EQ(AString("xxaxx").trim('x'), "a");
// only the given symbol is removed
EXPECT_EQ(AString("\t a \n").trim(), "\t a \n");

... or any symbol of a set:

EXPECT_EQ(AString("\t a b \r\n").trim(" \t\r\n"), "a b");
EXPECT_EQ(AString("xyhixyx").trim("xy"), "hi");

Note

Trimming returns an AStringView into the original string and doesn't allocate. Don't let the view outlive the string it was made from. Construct an AString from the view if you need a copy.

Splitting and joining#

split() returns an AStringVector; join() of AStringVector does the opposite.

EXPECT_EQ("1x2"_as.split('x'), AStringVector({"1", "2"}));
auto parts = "a, b, c"_as.split(", ");     // { "a", "b", "c" }
AString joined = parts.join('-');          // "a-b-c"

Passing strings around#

Take AStringView for read-only parameters. It converts from AString, std::string_view and string literals without copying. Return or store AString when you need ownership.

bool isGreeting(AStringView text) {
    return text.startsWith("Hello");
}

Printing#

AString can be streamed to std::ostream and to the logger, and is accepted by fmt as is.

ALogger::info("MyApp") << "Hello, " << name;

See also Binary data, compression and identifiers for raw bytes and Base64, and i18n for translating user-visible strings.