LINUX.ORG.RU

Правильный(???) стиль работы со строковыми данными на С

 ,


2

4

Иногда (прямо сейчас) приходится обрабатыавть строки на C.
Меня коробит от громоздкости операций выделения памяти, конкатенации и самое главное это snprintf с проблемой размера буфера под конечную строку, гигантское поле для выращивания вских мемориликов по невнимательности.

К примеру, размеры mult1_str, mult2_str и equal_str известны, нужно выделить память под всю строку:

snprintf(
    buf,
    buflen,
    "%s miltilple %s equals %s",
    mult1_str,
    mult2_str,
    equal_str
    );
варианты:
- махнуть шашкой и сделать килобайт на стеке ( ((( )
- ничем не размахивать и посчитать руками. (еще хуже)
- написать функцию которая будет вычислять длину «%s miltilple %s equals %s» без символов подстановки (уже лучше)
- отказаться от snprintf и собирать строку пачкой конкатенаций с аллокациями памяти и прочим...

А как делаешь ты?


Ответ на: комментарий от Deleted

К сожалению что в Qt, что в C# куча классов плохо делают свое дело, приходиться выкидывать поделки из Qt и брать нормальные библиотеки. Ну оставим GUI, это ужос, QML мне вот понравился больше, но он для телефонов как я понял.

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

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

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

Сочувствуешь более удобной работе со строками и массивами,чем в Си? Не всем же быть мазохиста и, как ты.

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

Учитывая то что у С есть библиотеки для строк, Fortran становится еще более плох, я на нем писал.

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

Ну оставим GUI, это ужос

Так я ж тебе о том что GUI как раз таки и не ужас там. Несколько лет назад мне нужно было сделать окошко с кнопочками и мне сказали что есть такое MFC. Я его открыл, получил травму, закрыл и сделал бэкэнд на Visual Basic. Qt меня исцелил от рваной раны детства)) В Qt GUI естественен для понимания.
А QML-да. Разрабы всяких окон модных молятся на него

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

MFC

Брр, ну хуже этого нет, посмотри на WPF теперь! Вот это я понимаю. А Qt хоть и лучше MFC, но до WPF ему далеко... К сожалению.

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

Если ты о том, что в С++ «строки» - это класс, то да, нет. Но этот класс хотя бы позволяет более удобно (кто сказал trim?) работать со строковыми данными, чем в C.

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

У Фортран тоже есть библиотека для строк (stringifor), но даже без неё он лучше с ними работает, чем С.

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

Я знаю, что в Си++ нет типа строки, а есть класс, реализующий работу со строковыми данными, посредством обёртки над массивом байт. И я в курсе, что функции trim там до сих пор нет (это был сарказм об удобстве, с казалось бы одним из самых востребованных действий со строкой).

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

Я знаю, что в Си++ нет типа строки, а есть класс

Тип, класс - роли не играет. Его в любом случает нет.

реализующий работу со строковыми данными

Реализовывать что-либо не обязательно. Нужна возможность иметь общий для всех либ тип/класс. В том же Rust такой тип+класс есть, но ничего кроме хранения он по сути и не предоставляет (trim таки есть, но не in-place, почему-то).

одним из самых востребованных действий со строкой

Так trim зависит от кодировки. А значит нужен тип/класс с поддержкой кодировки. А его нет.

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

Не в std, а в бусте. Ещё и от локали зависит. Удачного дебага.

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

Зачем мне сравнивать сторонние библиотеки, если я сравниваю стандартные библиотеки языков? Я серьёзно, как раз на работе меня просили написать модуль для парсинга строки на Фортране и я был очень-очень рад, что это был не Си, так как всё оказалось очень просто и без сторонних библиотек.

Ну глянул на gstring, чем он удобнее stringifor? Ну разве что первый опакечен (для linux только) и на его разработку с меньшей вероятностью забьют? Но на какое-то время и на разработку blitz++ забивали.

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

чем он удобнее stringifor?

Тем что его можно использовать не только в «COBOL'е для вычислений».

для linux только

msys2 -> pacman -S glib

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

я был очень-очень рад, что это был не Си, так как всё оказалось очень просто и без сторонних библиотек

За такой код убиват только! Лучше хотя бы регулярками, как это потом править?

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

За такой код убиват только!

За код без сторонних библиотек и использование сходу встроенных scan и verify для проверки хотя бы наличия недопустимых символов?

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

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

В С еще есть lex + yacc!

В или для?

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

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

Из детсада работников набирают туда что ль?

В или для?

Без разницы, главное что есть!

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

Тем что его можно использовать не только в «COBOL'е для вычислений».

Но зачем использовать Фортран не для вычислений? Вряд ли кто «блокнот» на нём попросит писать

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

Без разницы, главное что есть!

Очень большая разница.

Из детсада работников набирают туда что ль?

Туда, где совсем не документируют код? Наверное.

grem ★★★★★
()

варианты:
- махнуть шашкой и сделать килобайт на стеке ( ((( )
- ничем не размахивать и посчитать руками. (еще хуже)
- написать функцию которая будет вычислять длину «%s miltilple %s equals %s» без символов подстановки (уже лучше)
- отказаться от snprintf и собирать строку пачкой конкатенаций с аллокациями памяти и прочим...

Ну, если максимальный размер сообщения заведомо известен, то конечно первый вариант - «на стеке». Но с небольшим уточнением: используем n-безопасные варианты функций работы со строками и заворачиваем буфер вместе с соответствующими командами в анонимный блок - чтобы память не транжирить.

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

я не о скорости, а о удобстве

Тогда не используй сишку.

anonymous
()

Какой вариант в итоге то был выбран?

p.s.
Срачи - это местный колорит, не обращай внимания.

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

Если есть возможность использовать _GNU_SOURCE, то asprintf(). Так же посмотрел маны фряхи, есть он и там, но немного подругому ведет себя в случае ошибки.
Когда нет возможности, пока что решил брать длину форматной строки + сумма максимальных длин составляющих. Решение так себе. Есть вариант использовать двойной вызов snprintf, это не менее ужасно, но приемлемо

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

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

grem ★★★★★
()

Можно вызвать snprintf с NULL и нулевой длиной буфера, он вернёт тогда сколько нужно байт под буфер. Аллоцировать буфер (иногда можно с помощью alloca, хотя осторожно) и повторить вызов.

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

Можно же очень легко написать свой asprintf на двойном вызове snprintf. Всегда так делаю.

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

Если под строкой подразумевается произвольный набор байт - то был.

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

Ждём и надеемся. Потом ждём ещё лет 10, пока на него перейдут все либы.

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

Так trim зависит от кодировки

Да ну? Почему в Фортране тогда trim есть?

А значит нужен тип/класс с поддержкой кодировки. А его нет.

Но при этом реализуется самописными костылями через встроенные для std::string функции, например, erase без всяких привязок к кодировке.

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

Да ну? Почему в Фортране тогда trim есть?

Я хз как устроен фортран. Не уверен что ␣ во всех кодировках имеет тот же индекс. Тем более в unicode, whitespace это не только ␣.

Но при этом реализуется самописными костылями через встроенные для std::string функции, например, erase без всяких привязок к кодировке.
erase

Опять вернулись к std::vector<char>. Сегодня у вас ASCII, а завтра какая-нибудь срань, и ваша функции удалит левые символы.

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

Мне всегда казалось что в си работа с юникодом достаточно прозрачна в таком разрезе. Для trim, просто побайтово сравниваете символы с конца и с начала с неким перечнем пробельных символов, это работает со всеми пробелами из таблицы ascii (они однобайтные). Можно также дёргать isspace. С мультибайтными проблеами сложнее... Наверное, iswspace не будет работать? Мне приходилось городить код на основе icu, когда такая задача возникала. Едва ли существует другая библиотека, позволяющая решить данную задачу.

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

Под «юникодом» я понимаю утф-8, где невозможно знать где начинается один символ и кончается другой, для утф16 есть маркеры что данный 2-байтный символ не полный. Один символ вполне может быть более 4 байт.

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

просто побайтово сравниваете символы

В unicode так делать нельзя.

Под «юникодом» я понимаю утф-8, где невозможно знать где начинается один символ и кончается другой

Шта?

Один символ вполне может быть более 4 байт.

4-байта - это code point. Символ - слишком абстрактное понятие. Для меня символ - это графема. Она может содержать сколько угодно байт.

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

Ну можно же, просто читаете в цикле с конца (если размер данных известен), если это пробел, двигаете указатель. Когда проблелы кончились, присваиваете \0 байт концу строки. Другой вариант, это только перебрать всю строку. Тот же utf-8 совместим c однобайтным ascii, и с его данными в некоторой степени можно работать как с обычными asciiz строками.

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

Хотя Вы правы, нас вероятно постигнет неудача, если в конце строки окажется не ascii символ, а, скажем, буква Р из кириллического алфавита (что она там забыла?) http://www.fileformat.info/info/unicode/char/420/index.htm

В таком случае, только перебор всей строки. Это довольно долгая операция и работать придётся с utf16-utf32.

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

С этим-то как раз всё ок, так как второй байт в code point будет начинаться с 10xx xxxx.

Я же говорил о том, что в unicode «пробелов» много. И большинство из них не однобайтные.

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

Буква Р, хотя и имеет кодпоинт 0420, но в utf8 она кодируется как d0 a0.
Отсюда вывод — вы не знаете ни устройства, ни основных свойств кодирования в utf8 (намекну — это префиксный код, способный к автосинхронизации), поизучайте на досуге предмет, прежде чем делать нелепые суждения публично ;)

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

Ну это был лишь пример, аппроксимация знаете ли. Я наскидку так не скажу, может ли попасться в каком-нибудь алфавите совпадение с пробелом, но выглядит достаточно опасно.

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

По-моему однобайтные символы всё же кодируются одним байтом в utf-8, так что всё ещё актуально.

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