Строка "foo" кодируется как 66 6F 6F, но это похоже на почти все производные ASCII. Это одна из главных особенностей UTF-8: обратная совместимость с 7-битным ASCII. Если вы имеете дело только с ASCII, вам не нужно делать ничего особенного.
Другие символы кодируются до 4 байтов. В частности, биты кодовой точки Unicode разбиты на один из шаблонов:
- 0xxxxxxx
- 110xxxxx 10xxxxxx
- 1110xxxx 10xxxxxx 10xxxxxx
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
с требованием использования кратчайшей подходящей последовательности. Так, например, знак евро ('€' = U + 20AC = двоичный 10 000010 101100) кодируется как 1110 0010, 10 000010, 10 101100 = E2 82 AC.
Итак, просто пройтись по кодовым точкам Unicode в строке и кодировать каждую из них в UTF-8.
Сложнее всего понять, в какой кодировке находится ваша строка. Большинство современных языков (например, Java, C #, Python 3.x) имеют разные типы для «байтового массива» и «строки», где «строки» всегда имеют одинаковую внутреннюю кодировку (UTF-16 или UTF-32), и вы необходимо вызвать функцию «кодирования», если вы хотите преобразовать ее в массив байтов в определенной кодировке.
К сожалению, более старые языки, такие как C, объединяют "символы" и "байты". (IIRC, PHP тоже такой, но прошло несколько лет с тех пор, как я его использовал.) И даже если ваш язык поддерживает Unicode, вам все равно придется иметь дело с дисковыми файлами и веб-страницами с неопределенными кодировками. Для более подробной информации, ищите "chardet".