LINUX.ORG.RU

Корректен ли мой вариант решения задачи (поиск подстроки в строке)?

 , ,


0

1

Решил задчку 4.12 из «Программирование: введение в профессию. Задачи и этюды» А.В. Столярова:

"Напишите функцию, которая в заданной строке находит вхождение заданной подстроки и, если таковое обнаружено, возвращает адрес (не индекс в массиве, а именно адрес!) начала подстроки в строке, если же заданная подстрока не обнаружена - возвращает NULL. Используя эту функцию, напишите программу, которая принимает на вход произвольное (не менее одного) количество аргументов командной строки, первый из них рассматривает как подстроку, и печатает:

a) те из оставшихся аргументов, которые содержат в себе эту подстроку (каждый на отдельной строке);

b) для каждого из аргументов, содержащих в себе подстроку - сам этот аргумент и количество вхождений в него указанной подстроки.

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

Программа получилась следующей:

/* num_of_pat_occur_in_cmd_line_args.c */

#include <stdio.h>
#include <string.h>

enum boolean { false, true };

enum boolean fit(const char *str, const char *pat)
{
    for (; *pat; str++, pat++) {
        if (*str != *pat)
            return false;
    }
    return true;
}

const char *addr_of_occur(const char *str, const char *pat)
{
    for (; *str; str++) { 
        if (*str == *pat) {         /* start of pattern occurence */
            if (fit(str, pat))      /* checking if the rest of the pattern */
                return str;         /* match */
        }
    }
    return NULL;
}

int num_of_pat_occur(const char *str, const char *pat)
{
    int n = 0;
    while ((str = addr_of_occur(str, pat))) {
        str = str + strlen(pat);
        n++;
    }
    return n;
}

int main(int argc, char **argv)
{
    char *pat;
    int i;
    if (argc == 0) {
        printf(
            "Please provide command line arguments: pattern and string(s)\n"
        );
        return 1;
    }
    pat = argv[1];
    for (i=2; i < argc; i++) {
        int n = 0;
        n = num_of_pat_occur(argv[i], pat);
        if (n > 0)
            printf("%s - %d;\n", argv[i], n);
    }
    return 0;
}

Однако меня смущает тот факт, что в функции num_of_pat_occur я использовал strlen, хотя автор говорит что «можно вообще не обращаться к элементам строк ни напрямую, ни как-то ещё, кроме как через написанную (вами) функцию».

Возможно я что-то упустил?

Хм, по идее если у тебя нульиерминейтед строки, то достаточно найти адрес конца строки и посчитать длину.

ЗЫ. Но вообще твой код в общем случае работать не будет.

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

хотя автор говорит что «можно вообще не обращаться к элементам строк ни напрямую, ни как-то ещё, кроме как через написанную (вами) функцию

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

goingUp ★★★★★
()

Однако меня смущает тот факт, что в функции num_of_pat_occur я использовал strlen, хотя автор говорит что «можно вообще не обращаться к элементам строк ни напрямую, ни как-то ещё, кроме как через написанную (вами) функцию».

По логике работы функции длина подстроки вообще не изменяется. Вы может рассчитать её один раз в начале функции:

const size_t patl = strlen(pat);

и переиспользовать. На ассимптотику никак не влияет, но чуток сэкономить времени можно если подстрока длинная.

dsl
()

Забавно. Вас попросили strstr() заимплементировать. Если libc’шную дёргать нельзя - следующая лучшая вещь это комбинация memchr() + memcmp(). Если с этим разберётесь - остальное действительно тривиально делается. Единственный catch - в (б) видимо ожидается что «aaa» входит в «aaaa» дважды так как про «непересекающиеся» ничего не сказано, будьте аккуратней.

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

Утф32 и никаких гвоздей. Ещё и производство оперативки подстегнуть. Нуль терминатор - говно на палке. БЕ не нужно

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

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

Во-вторых, utf8 устроен так что значительная часть операций, работающих с ascii строками, к нему также применима. Например, конкатенация, поиск, разбивка по вхождению подстроки и многое другое, причём и для null-terminated, и для span, и для паскалевских строк. Поэтому для большинства применений для поддержки utf8 вообще ничего не надо делать. Сложности начинаются только когда нужно работать с отдельными лексемами/буквами/знаками/символами/кодпоинтами.

slovazap ★★★★★
()
Ответ на: комментарий от ya-betmen

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

т.е. ты объясняешь как заменить использование strlen своим собственным кодом. Это мне понятно. Не понятно удовлетворяет ли это условию задачи «никакой дополнительный анализ строк здесь не нужен».

Но вообще твой код в общем случае работать не будет.

Ты не мог бы подсказать пример, когда код не будет работать корректно? Вроде в ходе беглого теста всё соответствует условию задачи.

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

Ну т.е. возвращать не адрес начала подстроки в строке, а адрес первого символа после окончания подстроки? Я об этом думал, но с условием задачи не сходится же :) Я даже полез проверять нет ли в этом месте в условии задачи ошибки в список опечаток на сайте автора - нет, всё верно (http://stolyarov.info/books/programming_intro/taskbook)

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

никакой дополнительный анализ строк здесь не нужен

Это уж как считать дополнительность, при твоём раскладе он необходимый, в том или ином виде оно будет присутствовать.

когда код не будет работать корректно?

Когда на входе будут строки с переменым числом байт на символ и неоднозначностью записи символов, которые почему-то стали стандартами.

ya-betmen ★★★★★
()