Тип string является специализацией шаблона basic_string для элементов типа char и определен как:
typedef basic_string<char> string;
Шаблон basic_string предоставляет типы и методы, схожие с теми, что предоставляют стандартные контейнеры, но он имеет много специфических методов, которые позволяют достаточно гибко манипулировать как строками, так и их частями (подстроками). Минимизация операций копирования строк, которой гордится MFC-класс cstring, на самом деле приводит к труднообнаруживаемым и невоспроизводимым (irreproducible) ошибкам, которые очень сильно портят жизнь программистам. Я с интересом узнал, что члены комиссии по утверждению стандарта C++ анализируют ошибки, возникающие из-за совместного использования двумя переменными строкового типа одной и той же области памяти, и пытаются выработать спецификации относительно времени жизни ссылок на символы строки. Если вы запутались в этой фразе, то следующий фрагмент программы, который комиссия использует в качестве теста, должен прояснить ситуацию. При выполнении он выведет строку «Wrong» или «Right», что означает, что ваша реализация string ненадежна или, скорее всего, надежна. Если она выведет строку «Right», то это еще не означает, что ваша реализация надежна. Ошибки могут всплыть в многопоточных приложениях, когда разные потоки работают с одной строкой символов:
//====== Две тестовые текстовые строки
string source("Test"), target;
//====== Ссылка на второй символ в строке
char& с = source[1];
//=====- Если данные не копируются при присвоении
target = source;
//====== то это присвоение изменит обе строки
с = ' z ' ;
//====== Этот тест позволяет выяснить ситуацию
cout « (target[l] == 'z1 ? "\nWrong" : "\nRight");
Здесь мы использовали ссылку, но аналогичное поведение обнаруживает и итератор. Вы можете объявить и использовать его так:
string::iterator it = source.begin()+1; *it = z1 ;
В рассматриваемой версии Studio.Net я с удовлетворением отметил, что тест выводит строку «Right».
Следующий фрагмент демонстрирует технику обрезания «пустого» текста в начале и конце строки. Она не очень эффективна, но вполне пригодна для строк небольшого размера:
//====== Множество пустых символов
char White " \n\t\r";
//====== Ищем реальное начало строки
//====== и усекаем лишние символы слева
s = s.substr(s.find_first_not_of(White));
//====== Переворачиваем строку и повторяем процедуру
reverse (s .begin () , s.endO);
s = s.substr(s.find_first_not_of(White));
//====== Вновь ставим строку на ноги
reverse (s .begin (), s.end());
Интересный пример, иллюстрирующий работу со строками, я увидел в MSDN. Нечто вроде секретного детского языка под названием Pig Latin (свинячья латынь). Алгоритм засекречивания слов состоит в том, что от каждого слова отрывают первую букву, переставляют ее в конец слова, а затем добавляют туда окончание «ау». Игра, очевидно, имеет свою историю. Приведем коды функции, которая реализует этот алгоритм и возвращает засекреченную строку:
//====== Преобразование строки по принципу Pig Latin
string PigLatin (const strings s)
{
string res;
//======= Перечень разделителей слов
string sep(" .,;:?");
//======= Длина всей строки
uint size = s.lengthO;
for (uint start=0, end=0, cur=0; cur < size; cur=end+l)
{
//==== Ищем позицию начала слова, начиная с cur
start = s.find_first_not_of(sep, cur) ;
//==== Копируем разделители между словами
res += s.substr(cur, start - cur) ;
//==== Ищем позицию конца слова, начиная со start
end = s.find_first_of(sep, start) ;
//==== Корректируем позицию конца слова
end = (end >= size) ? size : end - 1 ;
//==== Преобразуем по алгоритму
res += s. substr (start-t-1, end-start) + s [start] +"ay"; )
return res;
}
Проверьте работу алгоритма с помощью следующего теста, который надо вставить внутрь функции main:
string s("she,sells;
sea shells by the sea shore");
cout « "Source string: " « s « endl;
cout « "\nPig Latin(s): " « PigLatin(s);
В результате вы увидите такой текст:
Source string: she,sells;
sea shells by the sea shore
Pig Latin(s): hesay,ellssay;
easay hellssay ybay hetay easay horesay