Обработка строк в C++
Стандартная библиотека C++ |
---|
Контейнеры |
Стандартная библиотека C |
Язык программирования C++ поддерживает обработку строк , в основном реализованную в стандартной библиотеке . Стандарт языка определяет несколько типов строк, некоторые из которых унаследованы от C , некоторые предназначены для использования функций языка, таких как классы и RAII . Наиболее часто используемым из них является станд::строка .
Поскольку первоначальные версии C++ имели только «низкоуровневые» функции и соглашения по обработке строк C , за прошедшие годы было разработано множество несовместимых проектов для классов обработки строк, которые до сих пор используются вместо std::string
, и программистам C++ может потребоваться обработка нескольких соглашений в одном приложении.
История
[ редактировать ]The Тип std::string является основным строковым типом данных в стандарте C++ с 1998 года, но он не всегда был частью C++. От C C++ унаследовал соглашение об использовании строк с нулевым завершением , которые обрабатываются указателем на их первый элемент, а также библиотеку функций, которые манипулируют такими строками. В современном стандарте C++ строковый литерал, такой как «Привет» по-прежнему обозначает массив символов, завершающийся NUL. [ 1 ]
Использование классов C++ для реализации строкового типа дает несколько преимуществ автоматического управления памятью и снижает риск доступа за пределами границ. [ 2 ] и более интуитивный синтаксис для сравнения и конкатенации строк. Поэтому был большой соблазн создать такой класс. На протяжении многих лет разработчики приложений, библиотек и платформ C++ создавали свои собственные несовместимые представления строк, например, в библиотеке стандартных компонентов AT&T (первая такая реализация, 1983 г.). [ 3 ] или Тип CString в Microsoft MFC . [ 4 ] Пока std::string стандартизированные строки, устаревшие приложения по-прежнему часто содержат такие пользовательские типы строк, а библиотеки могут ожидать строки в стиле C, что делает «практически невозможным» избежать использования нескольких типов строк в программах на C++. [ 1 ] и требовать от программистов выбора желаемого строкового представления перед началом проекта. [ 4 ]
В ретроспективе истории C++ 1991 года его изобретатель Бьярн Страуструп назвал отсутствие стандартного строкового типа (и некоторых других стандартных типов) в C++ 1.0 худшей ошибкой, которую он допустил при его разработке; «Отсутствие таковых привело к тому, что все заново изобретали велосипед и к ненужному разнообразию в самых фундаментальных классах». [ 3 ]
Проблемы реализации
[ редактировать ]Типы строк разных производителей имеют разные стратегии реализации и характеристики производительности. В частности, некоторые типы строк используют стратегию копирования при записи , при которой такая операция, как
string a = "hello!";
string b = a; // Copy constructor
фактически не копирует содержимое а к б ; вместо этого обе строки совместно используют свое содержимое, и счетчик ссылок на содержимое увеличивается. Фактическое копирование откладывается до тех пор, пока операция изменения, например добавление символа к любой строке, не приведет к различию содержимого строк. Копирование при записи может существенно повысить производительность кода, использующего строки (делая некоторые операции намного быстрее, а некоторые — намного медленнее). Хотя std::string больше не использует его, многие (возможно, большинство) альтернативных строковых библиотек по-прежнему реализуют строки с копированием при записи.
Некоторые реализации строк хранят 16-битные или 32-битные кодовые точки вместо байтов. Это было сделано для облегчения обработки текста в Юникоде . [ 5 ] Однако это означает, что преобразование в эти типы из std::string или из массивов байтов зависит от «локали» и может вызывать исключения. [ 6 ] переменной ширины Любые преимущества обработки 16-битных кодовых единиц исчезли, когда была введена кодировка UTF-16 (хотя преимущества все еще есть, если вам необходимо взаимодействовать с 16-битным API, таким как Windows). Qt QString является примером. [ 5 ]
Сторонние реализации строк также значительно различались по синтаксису извлечения или сравнения подстрок или выполнения поиска в тексте.
Стандартные типы строк
[ редактировать ]The Класс std::string является стандартным представлением текстовой строки, начиная с C++98 . Класс предоставляет некоторые типичные операции со строками, такие как сравнение, конкатенация, поиск и замена, а также функцию получения подстрок . Ан std::string может быть создан из строки в стиле C, а строка в стиле C также может быть получена из нее. [ 7 ]
Отдельные единицы, составляющие строку, имеют тип char , как минимум (и почти всегда) по 8 бит каждый. В современном использовании это часто не «символы», а части многобайтовой кодировки символов, такой как UTF-8 .
Стратегия копирования при записи была намеренно разрешена первоначальным стандартом C++ для std::string, потому что это считалось полезной оптимизацией и использовалось почти во всех реализациях. [ 7 ] Однако были и ошибки, в частности Оператор[] возвращал неконстантную ссылку, чтобы упростить перенос манипуляций со строками на языке C (такой код часто предполагал один байт на символ, и поэтому это, возможно, было не очень хорошей идеей!) Это позволило использовать следующий код, который показывает, что он должен сделать копию, хотя почти всегда он используется только для проверки строки, а не для ее изменения: [ 8 ] [ 9 ]
std::string original("aaaaaaa");
std::string string_copy = original; // make a copy
char* pointer = &string_copy[3]; // some tried to make operator[] return a "trick" class but this makes it complex
arbitrary_code_here(); // no optimizations can fix this
*pointer = 'b'; // if operator[] did not copy, this would change original unexpectedly
Это вызвало некоторые реализации [ который? ] отказаться от копирования при записи. Также было обнаружено, что накладные расходы в многопоточных приложениях из-за блокировки, необходимой для проверки или изменения счетчика ссылок, превышают накладные расходы на копирование небольших строк на современных процессорах. [ 10 ] (особенно для строк, меньших размера указателя). Оптимизация была окончательно запрещена в C++11 . [ 8 ] в результате чего даже прохождение std::string в качестве аргумента функции, а именно.
void print(std::string s) { std::cout << s; }
следует ожидать, что он выполнит полную копию строки во вновь выделенную память. Распространенной идиомой, позволяющей избежать такого копирования, является передача константной ссылки :
void print(const std::string& s) { std::cout << s; }
В C++17 добавлен новый string_view класс [ 11 ] это всего лишь указатель и длина данных, доступных только для чтения, что делает передачу аргументов намного быстрее, чем в любом из приведенных выше примеров:
void print(std::string_view s) { std::cout << s; }
...
std::string x = ...;
print(x); // does not copy x.data()
print("this is a literal string"); // also does not copy the characters!
...
Пример использования
[ редактировать ]#include <iostream>
#include <iomanip>
#include <string>
int main() {
std::string foo = "fighters";
std::string bar = "stool";
if (foo != bar) std::cout << "The strings are different!\n";
std::cout << "foo = " << std::quoted(foo)
<< " while bar = " << std::quoted(bar);
}
Связанные классы
[ редактировать ]std::string — это определение типа для конкретного экземпляра std::basic_string Класс шаблона . [ 12 ] Его определение можно найти в Заголовок <строка> :
using string = std::basic_string<char>;
Таким образом строка обеспечивает Функциональность Basic_string для строк, имеющих элементы типа чар . Есть аналогичный класс std::wstring , который состоит из wchar t и чаще всего используется для хранения UTF-16 текста в Windows и UTF-32 на большинстве Unix-подобных платформ. Однако стандарт C++ не налагает на эти типы никакой интерпретации как кодовых точек или кодовых единиц Юникода и даже не гарантирует, что wchar_t содержит больше бит, чем чар . [ 13 ] Чтобы устранить некоторые несовместимости, возникающие в результате wchar_t , в C++11 добавлены два новых класса: std::u16string и std::u32string (составленный из новых типов char16_t и char32_t ), которые представляют собой заданное количество бит на единицу кода на всех платформах. [ 14 ] В C++11 также добавлены новые строковые литералы из 16-битных и 32-битных «символов» и синтаксис для помещения кодовых точек Юникода в строки с нулевым завершением (стиль C). [ 15 ]
А Basic_string гарантированно будет специализироваться для любого типа с char_traits, сопровождающая его. Начиная с C++11, только чар , wchar_t , char16_t и char32_t . Необходимо реализовать специализации [ 16 ]
А Basic_string также является контейнером стандартной библиотеки , и поэтому алгоритмы стандартной библиотеки могут применяться к единицам кода в строках.
Критика
[ редактировать ]Дизайн std::string был приведен в качестве примера монолитного проектирования Хербом Саттером , который считает, что из 103 функций-членов класса в C++98 71 можно было бы отделить без потери эффективности реализации. [ 17 ]
Ссылки
[ редактировать ]- ^ Jump up to: а б Сикорд, Роберт С. (2013). Безопасное программирование на C и C++ . Аддисон-Уэсли. ISBN 9780132981972 .
- ^ Уаллин, Стив (2003). Практическое программирование на C++ . О'Рейли.
- ^ Jump up to: а б Страуструп, Бьярн (1993). История C++: 1979–1991 (PDF) . Учеб. Конференция ACM по истории языков программирования.
- ^ Jump up to: а б Солтер, Николас А.; Клепер, Скотт Дж. (2005). Профессиональный С++ . Джон Уайли и сыновья. п. 23. ISBN 9780764589492 .
- ^ Jump up to: а б Бланшетт, Жасмин; Саммерфилд, Марк (2008). Программирование GUI на C++ с помощью Qt4 . Пирсон Образование. ISBN 9780132703000 .
- ^ «Класс wstring_convert» . docs.microsoft.com . 3 августа 2021 г. Проверено 26 декабря 2021 г.
- ^ Jump up to: а б Мейерс, Скотт (2012), Эффективный STL , Аддисон-Уэсли, стр. 64–65, ISBN 9780132979184
- ^ Jump up to: а б Мередит, Алисдер; Бём, Ганс; Кроул, Лоуренс; Димов, Петр (2008). «Модификации параллельной обработки базовой строки» . ISO/IEC JTC 1/SC 22 /WG 21 . Проверено 19 ноября 2015 г.
- ^ «21334 — Отсутствие безопасности потоков, совместимой с Posix, в STD::basic_string» .
- ^ Саттер, Херб (1999). «Оптимизации, которых нет (в многопоточном мире)» . Журнал пользователей C/C++ . 17 (6).
- ^ «std::basic_string_view – cppreference.com» . ru.cppreference.com . Проверено 23 июня 2016 г.
- ^ «Справочник по C++ для Basic_string» . Cppreference.com . Проверено 11 января 2011 г.
- ^ Гиллам, Ричард (2003). Демистификация Unicode: Практическое руководство программиста по стандарту кодирования . Аддисон-Уэсли Профессионал. п. 714. ИСБН 9780201700527 .
- ^ «Документ C++11 N3336» . Открытые стандарты . Язык программирования C++, Библиотечная рабочая группа. 13 января 2012 года . Проверено 2 ноября 2013 г.
- ^ Страуструп, Бьярне (2013). Язык программирования C++ . Эддисон Уэсли. п. 179. Архивировано из оригинала 25 ноября 2015 года . Проверено 24 ноября 2015 г.
- ^ «char_traits — Справочник по C++» . Проверено 1 августа 2015 г.
- ^ Саттер, Херб. «Монолиты «Не натянуты» » . gotw.ca. Проверено 23 ноября 2015 г.