Вы получаете первый символ в строке.
Но похоже, что строка является строкой UTF-8 (или, возможно, некоторым другим многобайтовым символьным форматом).
Это означает, что каждый символ (глиф), который напечатан os, состоит из 1 (или более символов).
Если это UTF-8, то любой символ, находящийся за пределами диапазона ASCII (0-127), фактически состоит из 2 (или более символов), и код печати строки правильно его интерпретирует. Но для кода печати символов невозможно правильно дешифровать один символ, который больше 127.
Лично я считаю, что форматы символов с динамической шириной не очень хорошая идея для внутреннего использования в программе (они подходят для транспортировки и хранения), поскольку они значительно усложняют манипуляции со строками. Я бы порекомендовал вам преобразовать строку в формат с фиксированной шириной для внутренней обработки, а затем преобразовать ее обратно в UTF-8 для хранения.
Лично я бы использовал UTF-16 (или UTF-32 в зависимости от того, что такое wchar_t) (да, я знаю, что технически UTF-16 не является фиксированной шириной, но при всех разумных обстоятельствах обучения это фиксированная ширина (когда мы включаем песок) -скрипт тогда нам может понадобиться использовать UTF-32)). Вам просто нужно наполнить поток ввода / вывода соответствующим фасетом codecvt для автоматического перевода. Внутренним кодом можно манипулировать, поскольку отдельные символы используют тип wchar_t.