LINUX.ORG.RU
ФорумTalks

Как много стрингов имеется в C++

 , ,


0

3

Вольный перевод https://blogs.msmvps.com/gdicanio/2018/05/28/how-many-strings-does-c-have/ не претендующий на литературную точность.


(... OK, языковый адвокат придрался бы, посоветовав: «Как много строковых типов...», но я хотел более хитростный заголовок)

Если вы программируете на Python и видите нечто, заключённое в одинарные или двойные кавычки, у вас имеется стринг:


s = 'Connie'

Нечто похожее происходит в Java, со стринговыми литералами вроде «Connie», реализованными как экземпляры класса java.lang.String:

String s = "Connie";

Хорошо.

Теперь давайте (барабанная дробь) войдём в царство C++! И тут начинается веселье.

Итак, давайте рассмотрим эту простую строку кода на C++:

auto s1 = "Connie";

Каков тип s1?

std::string ? массив char[7] ? (эй, "Connie" - это шесть символов, плюс завершающий NULL)

... что-то другое?

Вы можете использовать свою любимую IDE и, наведя указатель мышки на имя переменной, получить выведенный тип. Visual Studio C++ IntelliSense намекает на то, что это const char*. Вау!

А что насчёт "Connie"s ?

auto s2 = "Connie"s;

Нет, это не множественное число от "Connie". И это не искаженный саксонский родительный падеж. Теперь s2 относится к типу std::string ! Спасибо тебе, operator""s включённый в C++14.

Мы уже закончили? Разумеется нет! Не забывайте - это C++!

Например у вас может иметься u8"Connie", который представляет юникодный UTF-8 литерал. И разумеется, нам необходимо обсуждение на StackOverflow для того, чтобы выяснить "Как u8-литералы должны работать".

А ещё не забудте о L"Connie", u"Connie" и U"Connie" которые представляют const wchar_t*, const char16_t* (закодировано в UTF-16) и const char32_t* (закодировано в UTF-32) соответственно.

Ну теперь мы закончили, правда? Ещё нет!

На самом деле вы можете сочетать приведённые выше префиксы со стандартным s-суффиксом, например: L"Connie"s - это std::wstring, а U"Connie"s - это std::u32string и так далее.

Закончили, правда? Ещё нет! На самом деле нам надо учесть ещё и сырые (raw) стринговые литералы. Например: R"(C:\Path\To\Connie)" который является const char* (константным символьным указателем) на “C:\Path\To\Connie” (что ж, это позволяет избавиться от кодирования '\' при помощи управляющей последовательности '\\').

Так же не забывайте о сочетании сырых стринговых литералов со всеми префиксами и s-суффиксом, разобранными выше. Например: LR"(C:\Path\To\Connie)", UR"(C:\Path\To\Connie)", LR"(C:\Path\To\Connie)"s, UR"(C:\Path\To\Connie)"s и так далее!

Ой и в добавок к стандартному классу std::string и прочим стандартным, основанным на std::basic_string, определениям строковых типов (таких как std::wstring, std::u16string, std::u32string и т.д.) имеются платформо/библиотечно зависимые классы, такие как CString, CStringA и CStringW в ATL/MFC, QString в Qt и wxString в wxWidgets.

Вау! Не удивлюсь, если я упустил какие-то другие вариации стрингов.

P.S. Со всем этим стринговым разнообразим (наверное слишком большим...), как насчёт добавления в стандартную библиотеку C++ некоторых удобных стринговых операций, как например обрезание пробелов или преобразование символов в заглавные или в строчные? При этом в стандартной библиотеке C++ уже имеются функции «для ракетостроения», такие как функции Бесселя. А ведь ещё в стародавние времена MFC там в CString уже были такие функции как Trim, MakeLower и MakeUpper и это далеко не весь список.

★★★★★

Последнее исправление: bbk123 (всего исправлений: 10)

Не раскрыта тема decltype(auto) s3 = "Connie";

Softwayer ★★
()

Не знаю, как там у Java, а в питоне, тоже есть raw-строки, иначе регулярки были бы сильной болью

SR_team ★★★★★
()

Так это и без плюсов так.
Если хочется юникода и операций над ним, изволь жонглировать библиотечными реализациями.

Беда всех языков, стартовавших с бедной стандартной библиотекой: все переизобретают велосипед.

aidaho ★★★★★
()
Ответ на: комментарий от SR_team

Да. Я просто запомнил что L обычно используется только в виндовом коде. В первую очередь из-за того, что wchar на винде 16байт, а не 32.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

В java есть StringBuffer.

И StringBuilder, и для всего этого есть общий интерфейс CharSequence с массой реализаций. Вот только обычно используют обычный String или преобразовывают к нему. А в C++20 добавили std::u8string а его поддержку в std::cout не добавили. Ну и нахрена он нужен?

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от bbk123

в C++20 добавили std::u8string а его поддержку в std::cout не добавили

а в чем его принципиальное отличие от std::string? Clang по дефолту использует UTF-8 в строках и "Connie" при компиляции эквивалентен u8"Connie"

SR_team ★★★★★
()
Ответ на: комментарий от Begemoth

Это тяжелое наследие ANSI C.

Да неужели? C++ создал этот стринговый зоопарк исключительно сам.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от SR_team

Принципиальное отличие в использовании нового базового типа char8_t, вместо просто char. То, что обычно char поддерживает работу с UTF-8 это UB, то есть неопределённое поведение. Ещё одно отличие char8_t от char в том, что первый ещё и беззнаковый.

Есть даже предложение не использовать и не рекомендовать использовать char8_t и std::u8string до появления нормальной поддержки этих типов в стандартной библиотеке C++23
https://yehezkelshb.github.io/cpp_proposals/sg20/P1747-dont-use-char8_t-yet-i...

bbk123 ★★★★★
() автор топика
Последнее исправление: bbk123 (всего исправлений: 1)
Ответ на: комментарий от RazrFalcon

Безотносительно сторонних библиотек, в С также есть

u" s-char-sequence " 	(3) 	(since C11)
U" s-char-sequence " 	(4) 	(since C11)
L" s-char-sequence " 	(5) 	

https://en.cppreference.com/w/c/language/string_literal

и есть предложение по char8_t в С23:

http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2231.htm

Так что совместимость в этом с С++ сохраняется…

fsb4000 ★★★★★
()
Ответ на: комментарий от Begemoth

А ничего, что wchar_t еще в С89 был? В этом тоже С++ виноват?

В отличии от char8_t в C++20 wchar_t в C89 - всего лишь typedef, то есть не базовый тип. А вот C++ этот wchar_t сделали уже базовым типом.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от RazrFalcon

А в C не зоопарк? В каждой либе свои строки.

Потому что стандартную библиотеку развивали бюрократы и потому что в более новых версиях C много чего тупо скопировано из C++.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от bbk123

Да неужели? C++ создал этот стринговый зоопарк исключительно сам.

Но косвенно, он порождение того факта, что в Си строк почти нет. А еще вот это zero terminated - оно конечно было удобно в работе с ленточными накопителями, но даже тогда где-то к концу 70-х давно надо было запилить нормальные строки и операции над ними.

praseodim ★★★★★
()
Ответ на: комментарий от bbk123

А что литералы типа wchar_t (L”…”) уже из С89 удалили?

ЗЫ: а так в этом убогоньком недоязычке даже bool и тот не базовый тип. Для справки, я в курсе, сколько на С написано, это ни делает его менее убогим, ни оправдывает его убогость.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

ЗЫ: а так в этом убогоньком недоязычке даже bool и тот не базовый тип. Для справки, я в курсе, сколько на С написано, это ни делает его менее убогим, ни оправдывает его убогость.

ЗЫ: а так этот убогонький недознаток даже не знает, что начиная с C99 _Bool – базовый тип. Для справки, я в курсе, сколько комментариев им написано (4742), это ни делает его менее убогим, ни оправдывает его убогость.

goto-vlad
()

я пишу жопой. но виноват С++.

salozar
()
Ответ на: комментарий от praseodim

Но косвенно, он порождение того факта, что в Си строк почти нет. А еще вот это zero terminated - оно конечно было удобно в работе с ленточными накопителями, но даже тогда где-то к концу 70-х давно надо было запилить нормальные строки и операции над ними.

Для того времени и преимущественно англоязычных стран или стран с почти таким же алфавитом этого более чем хватало. Но затем появилась необходимость в поддержке языков с нелатинскими алфавитами и тут началось. Куча дублирующих друг друга и несовместимых между собой кодировок для киррилицы, расширенные двухбайтовые кодировки язиатских языков. Куча костылей и подпорок вокруг всего этого. Для нормальных юникодных строк не было поддержки даже в железе. Вспомни TSR-ы под DOS для поддержки киррилицы. Они ведь, помимо всего прочего, ещё и перезагружали фонты в видеопамяти видеокарточки. А фонты там тоже расчитаны под 8 бит. В конце 70-х (IBM PC ещё не появился) сделать повсеместную поддержку чего-то похожего на юникод было невозможно. Полагаю, что и 80-е и даже в 90-е.

Вопрос в другом. Почему весь этот 8-и битный ад заботливо притащили в стандартную библиотеку C? Даже здесь на LOR-е долгое время пропагандировали примитивный koi8-r и локаль с ним. Для системных целей латинского алфавита более чем достаточно. Для прикладных задач нужно было использовать сторонние библиотеки или другие языки программирования. Язык C уже давно не годится для прикладного программирования и нечего ему там делать. В C++, благодаря наличию классов и объекто-ориентированной парадигмы, как раз наоборот, могли бы сразу или хотя бы в первом стандарте C++89 сделать нормальные юникодные стринги, как это сделали в Java.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от Begemoth

А что литералы типа wchar_t (L”…”) уже из С89 удалили?

Литералы на typedef-ный тип? Ты уверен?

ЗЫ: а так в этом убогоньком недоязычке даже bool и тот не базовый тип. Для справки, я в курсе, сколько на С написано, это ни делает его менее убогим, ни оправдывает его убогость.

Его придумали как альтернативу ассемблеру, когда дальнейшая разработка UNIX на последнем стала слишком трудоёмкой. Для системных нужд это вполне пригодный язык. Его просто замусорили всякими костылями для прикладных задач, для которых он всё равно не пригоден.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от praseodim

Буржуям на проблемы негров положить - у них всё работает. Мы в начале 00 пропихивали патч в одну из библиотек апача. Так убедить включить поддержку utf8 ни_в_какую, сошлись на том, что если есть не ascii символы - включаем utf8.

а так да, нужно было в программу на с++ сделать заплатку, чтоб поднять начальный символ в словах, а остальные сомволы в lowcase. То что на других языках делает за 10 секунд, на с++ в полном объёме не получилось т.к. там, понятное дело, какой-то utf8 велосипед. Понятное дело для русского и eng я накостылял, но для француского и японского не получилось. Зато читаешь нововведения … далёк комитет цпп от народа и его чаяний ой как далёк.

vtVitus ★★★★★
()
Последнее исправление: vtVitus (всего исправлений: 1)

Это просто разные строковые литералы, ну и что с того?

rumgot ★★★★★
()

как насчёт добавления в стандартную библиотеку C++ некоторых удобных стринговых операций, как например обрезание пробелов или преобразование символов в заглавные или в строчные

tolower() / toupper() есть.

rumgot ★★★★★
()

Неприкрытая зависть питонщиов и джавистов, вам такое разнообразие и не снилось!

filosofia
()
Ответ на: комментарий от rumgot

А какие там еще строки?

QLatin1String, QStringRef, QStringView

Есть ещё тип который можно использовать как строку: QByteArray

И QStringBuilder, но это internal класс, но доступ к нему тоже есть…

fsb4000 ★★★★★
()
Ответ на: комментарий от rumgot

tolower() / toupper() есть.

Которые умеют работать лишь с char? А для белых людей что-то другое есть?

bbk123 ★★★★★
() автор топика

Правильный ответ: В C++ нет строковых типов. Есть лишь пачка классов, которые пытаются имитировать строки.

atrus ★★★★★
()

А что ж ты хотел от этого убогонького недоязычка?

hateyoufeel ★★★★★
()

Кудахчущим про то, что в С нету типа данных для строк - а почему он вообще должен быть? С не скрывает сложность вещей от программиста, а строка это сложный составной объект, со стороками нельзя оперировать элементарными атомарными инструкциями. Каждый волен запилить себе библиотеку для работы со строками так, как ему нравится. Или использовать готовую стороннюю. Стандартная библиотека языка не обязана включать в себя всё на свете.

Harald ★★★★★
()
Ответ на: комментарий от fsb4000

Для белых людей. Нечто вроде std::transform(s.begin(), s.end(), s.begin(), ::tolower); придумали явно для BLM и прочих погромщиков.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от bbk123

Для белых людей. Нечто вроде std::transform(s.begin(), s.end(), s.begin(), ::tolower); придумали явно для BLM и прочих погромщиков.

Я понял, что ты не переходил по моей ссылке.

Кстати,

std::transform(s.begin(), s.end(), s.begin(), ::tolower); это UB

И об этом написано в cppreference, у Страуструпа и по моему даже в стандарте есть прям точь в точь такой пример.

Like all other functions from <cctype>, the behavior of std::tolower is undefined if the argument’s value is neither representable as unsigned char nor equal to EOF. To use these functions safely with plain chars (or signed chars), the argument should first be converted to unsigned char

std::string str_tolower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(), 
                // static_cast<int(*)(int)>(std::tolower)         // wrong
                // [](int c){ return std::tolower(c); }           // wrong
                // [](char c){ return std::tolower(c); }          // wrong
                   [](unsigned char c){ return std::tolower(c); } // correct
                  );
    return s;
}

А по моей первой ссылке был такой пример:

#include <locale>
#include <iostream>
 
void try_lower(const std::ctype<wchar_t>& f, wchar_t c)
{
    wchar_t up = f.tolower(c);
    if (up != c) {
        std::wcout << "Lower case form of \'" << c << "' is " << up << '\n';
    } else {
        std::wcout << '\'' << c << "' has no lower case form\n";
    }
}
 
int main()
{
    std::locale::global(std::locale("en_US.utf8"));
    std::wcout.imbue(std::locale());
    std::wcout << "In US English UTF-8 locale:\n";
    auto& f = std::use_facet<std::ctype<wchar_t>>(std::locale());
    try_lower(f, L'Σ');
    try_lower(f, L'Ɛ');
    try_lower(f, L'A');
 
    std::wstring str = L"HELLo, wORLD!";
    std::wcout << "Lowercase form of the string '" << str << "' is ";
    f.tolower(&str[0], &str[0] + str.size());
    std::wcout << "'" << str << "'\n";
}
fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от fsb4000

Я уже понял, что ты любишь придираться к мелочам. Речь шла об аналоге java.lang.String::toLowerCase. Мне не нравится писать кучу кода ради одного элементарного действия, такого, как преобразование символов строки в заглавные или строковые буквы.

Кстати, если s в моём примере - это std::u8string, то преобразовывать в unsigned char ничего не надо, поскольку char8_t уже unsigned.

bbk123 ★★★★★
() автор топика
Ответ на: комментарий от fsb4000

std::transform(s.begin(), s.end(), s.begin(), ::tolower); это UB

Вот за это я обожаю C++. Самый простой и очевидный путь наверняка содержит UB. А если не содержит, то его сломают скоро. Или он не работает с нужным типом строк. Или ещё какая-то дикая срань.

Просто потрясающий язык. Сразу видно, что авторы ненавидят программистов, себя и своих жирных мамаш, которые их породили.

hateyoufeel ★★★★★
()

Мы закончили?

Да. На первом же предложении. Потому что вот это вот всё — не плюсовые и не сишные проблемы. Я, как сишник и хейтер плюсов, заявляю тебе это в полном здравии и сознании. Твой тред вообще не про ЯП, а про кучу говностандартов локалей, под которые ЯП вынужден подстраиваться.

deep-purple ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.