LINUX.ORG.RU

Подскажите С-нубасу как банально распарсить строку в массив.

 ,


1

6

Приветствую. Казалось бы, тривиальная в 2023 году вещь - распарсить строку по делимитеру и загнать это в массив. Но нет, просто не будет.

Впрочем вопрос не в этом. Есть код. Описания переменных и прочая, я опущу, перейду сразу к сути.

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

printf("\n============ Parsing: ");printf(gtk_path);printf(" ========\n");
fg_color = "#888888";
bg_color = "#AAAAAA";

filePointer = fopen(gtk_path, "r");

while(fgets(buffer, bufferLength, filePointer)) {

 if (strstr(buffer, " fg_color ") != NULL)  {
  printf("\nFound fg_color entry: ");
  char **arr = split(buffer, ' ');
  fg_color = trim(arr[2]);
  printf(fg_color); // <<<<
 }

 if (strstr(buffer, " bg_color ") != NULL)  {
  printf("\nFound bg_color entry: ");
  char **arr = split(buffer, ' ');
  bg_color = trim(arr[2]);
  printf(bg_color); // <<<<
 }

}

fclose(filePointer);
printf("\n *** \n");printf("Main color: ");printf(fg_color);printf("BG color: ");printf(bg_color);printf("\n");


// Для информации приведу функции split и trim, хотя дело вряд ли в них.

char **split(char *str, char delim) {
  char **arr = malloc(sizeof(char *) * (strlen(str) + 1));
  int i = 0;
  for (char *p = strtok(str, &delim); p; p = strtok(NULL, &delim)) {
    arr[i++] = p;
  }
  return arr;
}

char *trim(char *s) {
    char *ptr;
    if (!s)
        return NULL;   // handle NULL string
    if (!*s)
        return s;      // handle empty string
    for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr);
    ptr[1] = '\0';
    return s;
}

Задача блока - прочитать CSS-файл, и выдрать с него полторы строчки в массив.

Сначала присваиваем двум переменным какое-то значение, например #888888 и #AAAAAA;

Читаем построчно файл, ищем в каждой строке нахождение подстроки, если подстрока найдена, тогда парсим ее в массив, и присваиваем нашей главной переменной значение массива с определенным индексом, и ГЛАВНОЕ - тут же выводим эту переменную на экран.

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

А вот что оно выводит:

============ Parsing: /usr/share/themes/Relax-Light-GTK/gtk-3.0/gtk.css ========

 *** 
Main color: #888888BG color: #AAAAAA

============ Parsing: /usr/share/themes/White - OE2-GTK/gtk-3.0/gtk.css ========

Found fg_color entry: #5C616C;
Found bg_color entry: #FAFAFA;
 *** 
Main color:  #000000;
BG color:  #000000;


Вопрос: откуда нах взялся этот #000000 ?

Когда подстрока не встречается в строке (т.е. файл не содержит ни fg_color ни bg_color) - возвращаются правильные значения того что я установил. Этот кусок работает правильно.

Да, возможно парсер работает криво, но ведь каждое присваивание fg_color = сопровождается printf'ом, и в пределах if'а с парсером видно, что парсер отрабатывает на отлично. #000000 - явно взято откуда-то из файла, но как оно могло пробраться в переменную, и не засветиться в выводе этой переменной ? ЧЯДНТ ?

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

Если в сишечке вопрос касается строк, всегда начинается содом и гоморра 😄. А если это не сишка, а плюсцы, то еще хуже, ибо каждый вспоминает про свой любимый сорт строк 😂.

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

Нет, следует писать puts("Hello, world!"); потому как возможности printf по форматированию строки тут не используются. Варианты printf("Hello, world!"); и printf("%s","Hello, world!"); в данном случае оба тоже подойдут, но первый будет бесполезно искать в строке Hello, world! символы процента, чтоб распарсить, а второй будет бесполезно парсить строку «%s» чтоб только узнать что надо сделать puts на вторую строку.

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

В языке Си нет работы со строками вообще. Вся его поддержка строк сводится к синтаксису двойных кавычек, и на этом всё. Всякие strcmp и прочее - это функции библиотеки, а не языка. К слову, тебе никто не мешает написать свои альтернативные, где будет что хочешь, включая другой формат представления (но тогда не получится красиво использовать двойные кавычки) и конкатенацию.

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

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

в си нет никаких «строк». есть указатель на char, который указывает на первый символ некоей области в памяти, которая должна восприниматься как текст, заканчивающийся нулем.

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

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

чтобы сконкатенировать две строки, надо взять новый буфер равный сумме длин строк + 1 символ(под ноль), скопировать туда первую строку(strcpy и дерривативы) и приписать вторую (strcat и деривативы).

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

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

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

Тогда нужен еще аллокатор со сборкой мусора.

wandrien ★★★
()

идейно поиск подстрок в строке.

функция strstr(«search_it», str) напускается на строку str. она возвращает позицию найденной подстроки. если она не нул - позиция найдена. затем найденная позиция увеличивается на длину искомой строки, чтобы встать в исходной строке на первый символ после найденной подстроки и так повторяется, пока strstr не даст null.

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

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

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

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

Нубы писали, что поделать. Правильное: не допускать нубов до программирования в серьёзных местах, но кто-то сделал плохой выбор в пользу скорости разработки.

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

Единственный язык из мне ведомых, где нет конкатенации.

обиженная ливерная колбаса (тм)

сишечка тебе неведома, не обольщайся.

способов штатными средствами сконкатенатить строки в этом ЯП даже не один. а уж про внештатные я даже вспоминать не буду.

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

способов штатными средствами сконкатенатить строки в этом ЯП даже не один. а уж про внештатные я даже вспоминать не буду.

Способ сконкатенатить строки может быть только один: str = str1+str2+str3+str4. Ну или как вариант str = str1.str2.str3.str4. Норм языки вообще могут $str = «$str1$str2$str3$str4».

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

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

Как я уже сказал выше - это дроч, а не сложение строк, еще и с кремом для рук из glib.

Банальное неизвестное количество str'ов сведет на нет весь блок кода, или потребует дополнительного костылинга.

А изящные вещи, возможные в других языках, и вовсе превратятся в кодо-лапшу. Пример:

print("Значение переменной = <".var.">\n")

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

Кстати, твои претензии навели меня на мысль как можно сделать примерно то, что ты хочешь, при этом оставаясь почти в рамках идеологии Си. Может быть, когда я допишу свой Си-компилятор (начал в 2019 и тогда же забросил, сделав препроцессор, парсер всего синтаксиса в структурированный формат в памяти и ещё немного), в нём это будет.

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

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

Cи позволяет обмазыватся своими реализациями строк

при этом есть стандартная библиотека с функциями для операций со строками

ТС не умеет в указатели - отсюда и его ошибки при жонглировании строк

асм не помещает знать -

а вот чего достаточно так представление о Си-машине - в которой указатели и есть фундамент

в представление о Си-машине как в той же К&R входит и представление что всё есть битовые последовательности заданной ширины - а вот знание асма не обязательно ибо pdp11 явно отличалось от x86 :)

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

Идеология в инструменте - это такое себе.

Да и само понятие «явно» давно уже нарушается. Почему инкремент i = i+1 можно записать как i++, а strcat(a1,a2) как a1+a2 - нет ?)

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

Cи позволяет обмазыватся своими реализациями строк

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

Работает - да.

Но выглядит уродски, нечитаемо, а кое где трудновыполнимо. Чото типа printf(strcat(malloc(str2,strlen(str1)),str1) вместо printf(str1.str2) - это банально мерзко.

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

Во-первых потому что i+1 это нативный оператор языка (компилируется в ассемблерное сложение) а strcat() - вызов функции из libc.so.6, а в Си нет перегрузки операторов чтоб назначить на них функции (в С++ есть). Во-вторых, потому что strcat делает совсем не то, что во всяких скриптах записывается как s1+s2 или s1.s2.

Идеология в инструменте - это такое себе.

Всё норм, именно из-за неё Си такой хороший для системных нужд.

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

Ещё раз, strcat - это не заменитель для s1+s2. И вообще на мой взгляд это устаревшая и плохая функция из 80-х которую лучше нигде не использовать. Правильно эта конструкция будет выглядеть так:

my_print(my_dynamic_strcat(str1,str2))

и при этом str1 и str2 будут не char const * а какого-то сложного структурного типа. Названия функций можно и покороче, выглядеть всё будет более-менее норм, хотя конечно не идеально. Вся сложность будет в реализации этих функций.

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

Ты такую дичь несёшь, что просто сам напросился. Поехали:

Банальное неизвестное количество str’ов сведет на нет весь блок кода, или потребует дополнительного костылинга.

А как ты собрался «складывать» (конкатенировать! неуч) «неизвестное количество строк» в синтаксисе a+b+c? А? Скажи мне. У тебя на уровне кода программы тупо вшито количество строк. Вот это повороооот. Никто не ожидааааал.

Для работы с «неизвестным количеством» чего-либо применяются…. Контейнеры. Например, массив!

А как в glib конкатенировать массив строк в строку? Для этого внезапно существует g_strjoinv().

А изящные вещи, возможные в других языках, и вовсе превратятся в кодо-лапшу. Пример:

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

А нормальные люди для форматирования строк применяют…. Средства форматирования строк, Иванушка-дурачок ты наш!

Ты вообще слышал про то, что программам нужна локализация? Про Gettext в курсе?

Как ты своё скриптовое говно локализовывать собрался?

Ты не спеши, подумой. Книжки почитай.

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

Ничего не мешает в Си написать и использовать вызов типа:

str_append(&str1, str2);

в качестве аналога для i+=j.

Впрочем, на этом этапе обычно берут готовую реализацию строк и не выёживаются.

Все претензии ТС по сути сводятся к тому, что он неуч без образования и не умеет программировать. Шире: не умеет мыслить системно.

Думает, что писать write-only лапшу на пхп - это означает разбираться в программировании.

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

Не прокатит, он хочет оператор а не функцию. А про возможность сделать это своей функцией (не strcat) я и так написал.

Впрочем, на этом этапе обычно берут готовую реализацию строк и не выёживаются.

Для этого «этапа» я написал две «готовые реализации» в своей си-библиотеке, и наверно десяток частных с захардкоженным поведением. Только всё это не «реализации строк» (кроме одной частной из недоделанного скриптового движка где такие строки неизбежны).

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

Книжки почитай.

Специально почитал. Зря. Убедился что всратая работа со строками только в С, как и дебилизм с указателями вместо копирования при присваивании.

А нормальные люди для форматирования строк применяют….

Нормальные люди пишут ОС ? Потому что если да, то вот тебе пример из официального источника, где конкатенация реализована ПРАВИЛЬНО:

string userName = "<Type your name here>";
string dateString = DateTime.Today.ToShortDateString();

// Use the + and += operators for one-time concatenations.
string str = "Hello " + userName + ". Today is " + dateString + ".";
System.Console.WriteLine(str);

str += " How are you today?";
System.Console.WriteLine(str);

Как ты своё скриптовое говно локализовывать собрался?

Очевидно как Microsoft в своем официальном примере на C#.

А как в glib конкатенировать массив строк в строку? Для этого внезапно существует g_strjoinv().

А кто тебе сказал что конкатенировать нужно весь массив в строку ?

в качестве аналога для i+=j.

Это не аналог, а велосипед.

Все претензии ТС по сути сводятся к тому, что он неуч без образования и не умеет программировать. Шире: не умеет мыслить системно.

А так же не умеют программировать создатели любых нормальных языков, которые ввели сложения строк на уровне операторов в своих ЯП. Oracle, Microsoft, IBM, Россум, Вирт, Лердорф - все лошары и не умеют программировать.

больным сознанием скриптовиков с нулём образования

Скриптовики - это те, кто хотят писать программы, а не героически изъеживаться в коде ? Ну да, тогда я скриптовик. Хотя вроде в 90хх паскаль еще не был скриптовым. А конкатенация уже была человеческой =)

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

Очевидно как Microsoft в своем официальном примере на C#.

То есть никак.

Твой уровень - это читать и писать «официальные» хелло-ворлды. «Писатель ОС», ору.

А кто тебе сказал что конкатенировать нужно весь массив в строку ?

Ты уже забыл, что собирался конкатенировать «неизвестное число строк»? Память больше одного сообщения не удерживает?

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

Чел, ты жалуешься буквально на то, что вместо A + B, нужно написать f(A, B, NULL).

Я не могу это всерьёз воспринимать.

Это всё по ведомству «В Паскале скобочки не той системы».

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

Я вчера сидел искал рейс-кондишены в многопоточном коде.

Кстати, не факт, что все нашел. И не факт, что верно исправлю. Может вместо одних гонок, добавлю другие гонки.

После этого твой ной слушать просто смешно.

«Дайте мне ЯП, который сам за меня будет многопоточный код писать! И вообще пусть за меня код пишет!»

Бгг)

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

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

Вот смотри, банальный пример

function make_css_style($fg_color, $bg_color)
{
return ".my_style {color:".$fg_color."; background-color: ".$bg_color.";}";
}

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

Более тебе скажу, написанный на питоне, он будет понятен пхп-исту, а написанный на паскале будет понятен и питонисту и пхп-исту.

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

Претензий к отсутствию этих фишек в С у меня нет. Есть задание - я его буду выполнять доступными способами включая костыли и подпорки к костылям.

Вот только не надо меня убеждать в том что а) в С это тоже есть; б) и в С это сделано удобно.

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

Вот смотри, банальный пример

Это срань господня, а не код:

." ".$ ." ".$ ."

Рельсы-рельсы, шпалы-шпалы, едет поезд запоздалый. Это читать - глаза болят.

Вот нормальный код:

char *s = g_strdup_printf(
    ".my_style {color: %s; background-color: %s;}",
    fg_color, bg_color
);

В разы читабельнее и чище.

Или то же самое на питоне:

s = '.my_style {color: %s; background-color: %s;}' % (fg_color, bg_color)

Вот только не надо меня убеждать в том что а) в С это тоже есть; б) и в С это сделано удобно.

У меня нет цели тебя убеждать. Это утиный синдром в чистом виде + неосиляторство. Лечится практикой. Ну или не лечится, тут как повезёт.

wandrien ★★★
()
printf("\n============ Parsing: ");printf(gtk_path);printf(" ========\n");

Не надо так делать. В переменной gtk_path могут встречаться %d, %i, %f и проч., и printf(gtk_path) распечатает тебе какую-то фигню.

А по поводу основного кода - я б это вообще не так писал.

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

Вот нормальный код:

Этот код как раз и является write-only, хотя бы потому что для того, чтобы поменять переменную после «{color: », тебе нужно идти в жопу строки. А если у тебя таких переменных 50 или 60 в строке ?

Скажи, а ты когда IRL в тетради делаешь записи - то тоже важные слова дописываешь в конце текста, а не КОНТЕКСТНО ?

Представляю твой ежедневник)))

8:00. Сходить купить "см. в жопу строки" в магазин на проспекте лампочку
10:00. Переустановить "см. в жопу строки" поставить "см. в жопу строки" скачать "см. в жопу строки" другу на ноуте винду, драйверы, новый фильм

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

Это утиный синдром в чистом виде

Если бы это было утиным синдромом, другие ЯП не имели бы в себе подобного функционала. Но они имеют. Даже тот древний TP5.0 родом из 80хх, на котором я программил еще на Поисках - умел в строки.

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

Не надо так делать. В переменной gtk_path могут встречаться %d, %i, %f и проч., и printf(gtk_path) распечатает тебе какую-то фигню.

Код не полный. Там есть и if (access(gtk_path, F_OK) == 0) {

А по поводу основного кода - я б это вообще не так писал.

Скажем так, то что я пишу - я недельку назад уже написал на питоне, оно работает на все 100%.

Конкретная задача - пролистать каталог GTK-шных тем, и выдрать с каждого gtk.css основной цвет, цвет фона и так далее.

На питоне это выглядит красиво и изящно, включая саму функцию выдирания цвета.

Но работа с ГТК на питоне - сущее зло, как впрочем и сам ГТК.

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

Код не полный. Там есть и if (access(gtk_path, F_OK) == 0) {

И? Что мешает существовать файловому пути со знаками процента в нём?

Но работа с ГТК на питоне - сущее зло, как впрочем и сам ГТК.

Поставь уже свой ник себе на комп, хватит себя мучать.

wandrien ★★★
()