LINUX.ORG.RU

А как комфортно работать со строками в современном C++?

 , ,


0

8

Привет, ЛОР.

Старый добрый std::string, как мы знаем, это по сути char* на стероидах. А во многих случаях надо работать со строками именно как со строками текста.

К примеру, в библиотеке QtCore, входящей во фреймворк Qt, есть класс QString. Её часто ругают за изобретение велосипедов. Но именно благодаря этой «фабрике велосипедов» я могу написать, например, так:

QString s;
QStringList sl;
...
if (sl.contains(s, Qt::CaseInsensitive)) {
    ...
}

И оно мне проверит наличие строки в списке, причём регистронечувствительным (второй параметр) способом. И не только для латинских символов. То есть если в списке есть «Капибара», в строке подойдёт как «Капибара», так и «капибара». Ещё есть split(), join() и дофига полезного.

А как такое сделать без QtCore, на голом STL? В C++20 появился некий std::u8string, он мне поможет, например?

★★★★★

Для простых задач - STL и самописные функции. Для сложных операций есть сторонние библиотеки (ICU, Boost.Nowide, fmtlib). В любом случае, пока не выйдет C++26, придётся писать много вспомогательного кода или прибегать к сторонним решениям.

basename
()

Я бы посмотрел в сторону библиотеки ICU. Если пишете кросс-платформенное приложение - std::wstring + локали (но это не самый лучший вариант, мне кажется).

std::u8string

контейнер для UTF-8, сам по себе не предоставляющий готовых функций для обработки текста

basename
()
Ответ на: комментарий от MirandaUser2

Только там похоже тоже нельзя явным образом культуру указать.

Вот правильное API поиска строк с возможностью указания локали.

usearch_openFromCollator()

https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/usearch_8h.html#a196d17b47e94225c0b74d3d9335c6c50

MirandaUser2
()
Ответ на: комментарий от basename

Интересно, как так получилось что за почти 40 лет разработки STL в C++ сосёт и требует от программиста написания вот этого вот:

придётся писать много вспомогательного кода

Вместо того, чтобы предоставить общеупотребимые и проверенные функции хотя бы для строк.

При этом libstdc++ уже представляет собой блоатварь на 2+ МБ. Если они таким образом пытались держать std-библиотеку компактной, то они явно обосрались.

STL как я погляжу вообще многие проекты на C++ не котируют и прямо пишут: используйте наши строки/контейнеры/примитивы вместо STL-трешака (Qt, wxWidgets, Chromuim/Blink, Firefox/Gecko и тд.)

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

NIH-синдром

Так это именно что STL располагает не только к развитию NIH-синдрома, но и к распространению кривофункций говнокода в кодовых базах различных проектов.

Ситуация:

В STL нет удобных split/join/contains по строкам и разрабы вынуждены писать кривые велосипеды вместо библиотечного варианта в котором были бы учтены подводные камни.

По итогу напердолившись с STL-велосипедами разработчики плюют на куцый (но при этом раздутый) STL и завязывают свой софт на какой-нибудь QtCore, который предлагает программистам функции и методы для быстрого и лаконичного решения ИХ проблем и не заставляет решать вместо них проблемы говённости библиотеки.

Вот буквально вчера смотрел код одной утилиты:

https://github.com/doomhack/GbaWadUtil

Пчёл там быстро настрочил код на QtCore и решил поставленную перед собой проблему. А мог бы сделать это на STL, если бы он не был дерьмовым.

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

так там локали существенное место занимают.

ICU includes a standard library of data that is about 16 MB in size. Most of this consists of conversion tables and locale information. The data itself is normally placed into a single shared library.

Update: as of ICU 64, the standard data library is over 20 MB in size. We have introduced a new tool, the ICU Data Build Tool, to replace the makefiles explained below and give you more control over what goes into your ICU locale data file.

https://unicode-org.github.io/icu/userguide/icu_data/#customizing-icus-data-library

MirandaUser2
()
Ответ на: комментарий от EXL

Требования к обратной совместимости. Ну и в С++ вообще всегда был упор на производительность, а не на удобство для разработчиков.

Chromuim/Blink, Firefox/Gecko

Ну да, там свои строки: WTF::String, nsString, чтобы, например, использовать нестандартные кодировки

libstdc++ уже представляет собой блоатварь на 2+ МБ

так при этом же и оптимизация под разные архитектуры (SSE, AVX, ARM Neon и т.д.), плюс инстанциации шаблонов

STL как я погляжу вообще многие проекты на C++ не котируют

локали, нет нативной поддержки Unicode, полезных методов :(

basename
()
Ответ на: комментарий от MirandaUser2

так там локали существенное место занимают.

Так почему эти локали и таблицы валяются не внутри /etc или /usr/share и используются в тех случаях, когда они нужны, а вкомпиливаются прямиком в SO-либу и после этого жрут огромное количество RAM, замедляя холодный старт программ? А если ещё вспомнить что некоторые проги любят эту ICU таскать с собой определённой версии…

Какая религия помешала сделать разработчикам ICU нормально?

Если я пользуюсь в данный момент времени ru_RU.UTF-8 и русским языком в программе – мне срать какие там правила COLLATE’ов в арабской вязи или какие символы заглавные и строчные в тайском письме. Если я переключаю язык или локаль то пусть и подгружаются нужные таблички.

Но нет, вместо этого развернём 28 МБ говённых таблиц в RAM всяких дохлоиероглифов и emoji, которые запакованы и распаковываются наверное во все 100 МБ, при этом горячим кодом и данными там будет 0.001%

Update: as of ICU 64, the standard data library is over 20 MB in size. We have introduced a new tool, the ICU Data Build Tool, to replace the makefiles explained below and give you more control over what goes into your ICU locale data file.

Ну да. Вместо того чтобы сделать ЕДИНУЮ мать его лёгкую либу и набор таблиц к ней в файлах, разработчикам прикладухи предлагают огромный костылище в виде НАРЕЗКИ сошки/дллки c нужными локалями, что плодит кучу сущностей по типу ICU-light.so, ICU-full.so, ICU-Cyrillic.so

Этот IT-мир точно свернул куда-то не туда.

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

И? Где нормальное UTF в стандарте?

Мелкомягкие в своей операционке так и не перешли на уникод и до сих пор сидят на кодовых страницах (тот же cp1251 в случае русской локали).

Ты думаешь корпы так стремятся к инновациям?

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

Пишут, что по памяти не должно быть заметных потерь.

Reducing the size of ICU’s data by eliminating unneeded resources can make sense on small systems with limited or no disk, but for desktop or server systems there is no real advantage to trimming. ICU’s data is memory mapped into an application’s address space, and only those portions of the data actually being used are ever paged in, so there are no significant RAM savings. As for disk space, with the large size of today’s hard drives, saving a few MB is not worth the bother.

https://unicode-org.github.io/icu/userguide/icu_data/#customizing-icus-data-library-for-icu-63-or-earlier

MirandaUser2
()
Ответ на: комментарий от WatchCat

Так я о том и говорю. Они и так каждый по отдельности - чудные ребята, а когда начинают спорить друг с другом... Чтобы добавить новые фичи в стандарт C++ нужны годы (а то и десятилетия) обсуждений / предложений / голосований.

basename
()

Всё-таки, когда нужно быстро написать код и чтобы «просто работало», то Qt остается самым адекватным выбором. Хотя бы потому, что можем отключить ненужные фичи на этапе сборки.

basename
()

и эти же люди верещат о том что java говно, хотя буквально на голом jdk можно написать софт ЛЮБОГО уровня сложности, без использования костылей типа boost и прочей крестопараши

anonymous
()

Ещё есть split(), join() и дофига полезного.

…в std::ranges:

#include <iomanip>
#include <iostream>
#include <ranges>
#include <string_view>
 
int main()
{
    using std::operator""sv;
    constexpr auto words{"Hello^_^C++^_^20^_^!"sv};
    constexpr auto delim{"^_^"sv};
 
    for (const auto word : std::views::split(words, delim))
        // with string_view's C++23 range constructor:
        std::cout << std::quoted(std::string_view(word)) << ' ';
    std::cout << '\n';
}
dataman ★★★★★
()
Ответ на: комментарий от cobold

Иначе говоря, существует ли что либо помимо icu и биндингов к нему?

Например, uni-algo 0.8.0 - библиотека алгоритмов Unicode для C++.
Но в ней пока ещё Unicode 15.1.0.

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

Ты думаешь корпы так стремятся к инновациям?

когда/если раст станет с удесятерённой скоростью замещать весь плюсовый и сишный код, обязательно влезет корпа, которая форкнет мейнлайн раст, появятся разные компилаторы, потом придётся создавать коммитет, и опять бюрократы будут тормозить прогресс, просто на новом витке развития

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

А могу ли я использовать мои плюсовые контейнерные алгоритмы с этим QStringList без лишних телодвижений?

QStringList наследуется от QList<QString>

Но зачем? Для регистронезависимого сравнения (или специфичных для Unicode операций) всё равно необходимы дополнительные адаптеры или предикаты. Пользоваться встроенными методами Qt как-то логичнее.

basename
()
Ответ на: комментарий от dataman

А теперь давай попробуем с помощью ranges, locales и facets сделать tolower для кириллической строки в UTF8 (ru_RU.UTF-8).

Вот это вообще жесть же, да ещё и небезопасно с памятью работает: https://en.cppreference.com/w/cpp/locale/codecvt/in

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

повсеместная протечка абстракций

чиста для сравнения(хотя C++ несравненен в своём метре колючей проволоки)

Python(CPython)/обьекты/модули/пространства_имён/С_подкапотный/бит_инлайнинг_для_асм_забав_находу

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

Интересно, как так получилось что за почти 40 лет разработки STL в C++ сосёт и требует от программиста написания вот этого вот: … Вместо того, чтобы предоставить общеупотребимые и проверенные функции хотя бы для строк.

Тут дело в ублюдочном Юникоде, который превратился из 2х байтового чара в ICU. wchar оперативно вкрутили всюду, но юникод и не думал останавливать «развитие» и превратился в говно. Сейчас интегрировать всё это безумие в STD - очень жирно. Никто уже толком не понимает как правильно парсить юникод, сравнивать. Полностью умеет (и то не факт) лишь ICU, но после этой мерзкой поделки хочется вымыть руки.

Имеем результат оверпереусложненного поделия, к которому мерзко прикасаться всем, так и живет оно в ICU, с иногда обертками вокруг него

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

utf-8 единственно верное

двухбайтовость заведомый палиатив с известными проблемами уже на момент выбора

ибо это не аски ситуация начала 60ых когда биты спарс

и более того сам факт размытия однобайтового двухбайтовым стандартом не то же самое как проваливание в байтовую потенциальную яму из семибитовости

так что utf8 навсегда

а то что индексы не совпадают со смещением - жалко фичу ну и туда ей и дорога - если реально нужно конверт из внешнего(медленного и битоберегущего) в среднее - рама(ибо кэш-биты тоже дороги)-представление тогда 4байтность и удобней и адекватней(если не октобайтность) современности

Картаго деленда

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

Да, сам по себе UTF-8 — неплохое решение проблемы. Просто в C++ для utf8/utf16-строк должен был быть предусмотрен forward-итератор, возвращающий в int декодированный юникодный символ, а сколько байтов при этом он прочитает — его внутреннее дело.

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

Разбудите меня, когда появится 100% точная функция, которая скажет, сколько знакомест занимает строка.

Пока что такой не существует, все вариации не 100% точные и не соответствуют последним стандартам (например когда емоджи должен смержиться в результате конкатенаций с другими эмоджи или когда используется много zalgo-like символов - с ними вообще ни один мессенджер нормально не дружит и в браузерах чаще всего нормально не отображается)

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

Меня удивляет как чисто текстовые библиотеки могут быть настолько жирнее GUI. Те же утилиты для рендеринга TeX занимают какие-то совсем неприличные объёмы на диске.

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

сколько знакомест занимает строка

Очевидно, это имеет смысл только для моноширинного шрифта. Поскольку в шрифтах прописана ширина каждого символа, то посчитать количество требуемых «знакомест» с учётом символов нулевой ширины технически возможно. Конкатенации эмоджи обрабатывать сложнее, там нужны специальные таблицы, например, https://unicode.org/Public/emoji/13.0/emoji-zwj-sequences.txt

Но это всё проблемы, связанные с интерпретацией символов юникода. К кодировке это не имеет отношения. Даже в UTF-32 была бы такая же проблема.

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

Дело не в utf-8, к ниму вопросов нет. Дело в самом ублюдском юникоде, его кодпоинтах и их комбинациях. Где одно и то же можно записать сотней разных способов, где миллион способов составить какую-то графему. Достаточно посмотреть на правила для подсчета графем в последовательности, это куча всяких ЕСЛИ И ИНАЧЕ, не говоря уже о адекватном сравнении всего этого говна. Либы, которые пилили энтузиасты (что я видел), ещё как-то умели итерацию по графемам, но ни о каком сравнении строк и речи не было.

Никто вокруг не умеет толком Юникод, ни терминалы, ни кто-то ещё. Сейчас точно можно сказать лишь одно - есть нормальный переносимый чистый ASCII, и весь остальной мусор. Ну и плюсовый стандарт вполне логично пуступил - признал ASCII, а на всё остальное смотрит как на дурдом

anonymous
()
Ответ на: комментарий от EXL

китайская нейронка вполне успешно может сплитов этих нагенерировать:

#include <vector>
#include <string>
#include <algorithm>

using namespace std;

vector<string> split(const string& s, char delimiter) {
    vector<string> result;
    string temp = "";

    for (char c : s) {
        if (c == delimiter) {
            if (!temp.empty()) {
                result.push_back(temp);
                temp.clear();
            }
        } else {
            temp += c;
        }
    }

    // Добавляем последнюю подстроку, если она есть
    if (!temp.empty()) {
        result.push_back(temp);
    }

    return result;
}

все программу дикпик сгенерировать не в состоянии, но кучу функций нагенерировать может, из которых потом легко склеить что-то

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

технически возможно.

Но ни одна программа, ни один браузер не может адекватно обработать конкатенацию или сдвиг всех описанных в unicode символов.

Ю̵̨̨̢̟̯͕͉͕̜̮͓̭̯͍̓ͅͅн̶̢̨̛̛͕͍͔̗͇̣͈͔͍̮̠̜͓̩̼̣̜̳̊̄̉̑̐́̐̎̓̃͋́͂͂̀͂͊̀̈̂̏̚͝͝͝и̷̱͂̌̍̐́̓̂͋͊̄͆̓͗̑̽͐̓̿͊͝к̶̧̡̨̳̮̣̝͇͎̟͙̩͇̬͚͕̘̺͇̻̦̈́̍͊ͅо̵̧̛͖͈͎͚̜͈̯̺̬͈͇̹̲̙̯̖̞̙͖̬̹͆̂̒͒̎̔͋̄̀̏̃̓̔͐̓̄͆̈͂̋̈́͠͠͝͠ͅд̸̨̨̥͒͛̓̍̎̑̍͗̽́̓͒̔̇͛̕͝͝͝ ̸̨̗͖͍̱̺̩̼̖̭̤̀͋̃̈̽̇́̀̔̂̋̿̈̈͜с̸̢̧̤̫͈͍̞͉̺̜̓̌̏̎͆̊̀̀͑́̑̋͘͠о̶̡̧̱̩̤̥̗͓́̿̕ͅс̴̧̡̭͕͙͕̺̖͉̪̟͈̘̯͇͓̻̱̼̻̙̲̀̆͐͆̈́̅͆̅̐́͆͊̎̈́́̊͐̐́͘͜͝͝͠͠͝ͅе̷̬̠̘̰̺̹̣͇͈̲̙̐̌̈́̓̾̉̅͗͆́͌̇͂̀т̷̡̥͉͍̫̝͍̰̮̣̦̠͇̪͍͇̮͙̦̈́͐̅̌̊́̾͑̌͐̾̈́̐͌̔̒̎̓͘͝ ̵̡̡̘̪̖͔͈͍̦̲͍͙̲̳̞̜̒̄͌͋́̐͆̐̓͑̐̈́̄̓̃͌̏̄̿̾͑̚͝͝͝ж̴̢̛̱̥͓̰̼͙̱̰͉̝̻̖̱̖͎̞̺̑͌̓̒͗͗̉̾̽̂̀̍̄̈́͗̏͋́͂͂̒̑̕͝͠о̷̢̢̛̩͕̬̜̰̹̪̙̣̰̯͇̭̼̣̜̹̣̜̙̘̗̜̺͌͊̀̽̓̑͛̄̋̉̄͆͘͘͘п̶̗̈́̈́̓͆ӱ̸̡̡̛͔͓̯̰͚͉͕͙̼̝̳̺̠͚̮̠͚͎͍̩̥́̇̉͆̌̍̃̆̿̽̎̊͆̾̌͋́͌̀ͅ

Ю̵̨̨̢̟̯͕͉͕̜̮͓̭̯͍̓ͅͅн̶̢̨̛̛͕͍͔̗͇̣͈͔͍̮̠̜͓̩̼̣̜̳̊̄̉̑̐́̐̎̓̃͋́͂͂̀͂͊̀̈̂̏̚͝͝͝и̷̱͂̌̍̐́̓̂͋͊̄͆̓͗̑̽͐̓̿͊͝к̶̧̡̨̳̮̣̝͇͎̟͙̩͇̬͚͕̘̺͇̻̦̈́̍͊ͅо̵̧̛͖͈͎͚̜͈̯̺̬͈͇̹̲̙̯̖̞̙͖̬̹͆̂̒͒̎̔͋̄̀̏̃̓̔͐̓̄͆̈͂̋̈́͠͠͝͠ͅд̸̨̨̥͒͛̓̍̎̑̍͗̽́̓͒̔̇͛̕͝͝͝ ̸̨̗͖͍̱̺̩̼̖̭̤̀͋̃̈̽̇́̀̔̂̋̿̈̈͜с̸̢̧̤̫͈͍̞͉̺̜̓̌̏̎͆̊̀̀͑́̑̋͘͠о̶̡̧̱̩̤̥̗͓́̿̕ͅс̴̧̡̭͕͙͕̺̖͉̪̟͈̘̯͇͓̻̱̼̻̙̲̀̆͐͆̈́̅͆̅̐́͆͊̎̈́́̊͐̐́͘͜͝͝͠͠͝ͅе̷̬̠̘̰̺̹̣͇͈̲̙̐̌̈́̓̾̉̅͗͆́͌̇͂̀т̷̡̥͉͍̫̝͍̰̮̣̦̠͇̪͍͇̮͙̦̈́͐̅̌̊́̾͑̌͐̾̈́̐͌̔̒̎̓͘͝ ̵̡̡̘̪̖͔͈͍̦̲͍͙̲̳̞̜̒̄͌͋́̐͆̐̓͑̐̈́̄̓̃͌̏̄̿̾͑̚͝͝͝ж̴̢̛̱̥͓̰̼͙̱̰͉̝̻̖̱̖͎̞̺̑͌̓̒͗͗̉̾̽̂̀̍̄̈́͗̏͋́͂͂̒̑̕͝͠о̷̢̢̛̩͕̬̜̰̹̪̙̣̰̯͇̭̼̣̜̹̣̜̙̘̗̜̺͌͊̀̽̓̑͛̄̋̉̄͆͘͘͘п̶̗̈́̈́̓͆ӱ̸̡̡̛͔͓̯̰͚͉͕͙̼̝̳̺̠͚̮̠͚͎͍̩̥́̇̉͆̌̍̃̆̿̽̎̊͆̾̌͋́͌̀ͅ

Ой, поломалося. Как же так?

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

я могу написать, например, так

И это по-твоему комфортно?

Смотри:

s = "Окунь"
sl = "Шлакоблокунь"
print(s.lower() in sl.lower())  # True
anonymous
()
Ответ на: комментарий от PPP328

А в принципе существует референс, как это должно выглядеть правильно? Кажется, тут виноваты сами шрифты, а не юникод. В шрифте Courier New вся диакритика в принципе распадается на отдельные символы с шириной.

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