3
votes

Besoin d'aide pour diviser une chaîne par délimiteur (et conserver le délimiteur dans la liste des jetons)

Je veux diviser une chaîne par un délimiteur et garder le délimiteur dans la liste des jetons

J'ai une fonction qui fait la même chose que strtok mais avec un délimiteur de chaîne (au lieu d'un ensemble de caractères) mais cela ne le fait pas ne garde pas le délimiteur et ne peut pas prendre un tableau de délimiteurs comme argument

Il s'agit d'une fonction qui divise une chaîne en jetons comme le fait strtok mais en prenant un délimiteur p >

static char *strtokstr(char *str, char *delimiter)
{
    static char *string;
    char *end;
    char *ret;

    if (str != NULL)
        string = str;
    if (string == NULL)
        return string;
    end = strstr(string, delimiter);
    if (end == NULL) {
        char *ret = string;
        string = NULL;
        return ret;
    }
    ret = string;
    *end = '\0';
    string = end + strlen(delimiter);
    return ret;
}

Je veux avoir un char ** split (char * str, char ** delimiters_list) qui divise une chaîne par un ensemble de délimiteurs et garde le délimiteur dans la liste des jetons

Je pense que j'ai également besoin d'une fonction pour compter le nombre de jetons afin de pouvoir malloc le retour de ma fonction split

// délimiteurs est un tableau contenant ["&&", "||" et NULL] split ("ls> file && foo || bar", délimiteurs) doit renvoyer un tableau contenant ["ls> file", "&&", "foo", "||", "bar "]

Comment cela peut-il être réalisé?


0 commentaires

3 Réponses :


1
votes
#include <string.h>
#include <stdio.h>
#include <stdlib.h>


char **split(char *str, char **delimiters, int number_of_delimiters, int *number_of_rows_in_return_array);

int main()
{

    char **split_str;
    char *delimiters[] = {
        "&&",
        "||"
    };


    int rows_in_returned_array;

    split_str = split("ls > file&&foo || bar && abc ||pqwe", delimiters, 2 , &rows_in_returned_array);

    int i;

    for (i = 0 ; i < rows_in_returned_array  ; ++i)
    {
        printf("\n%s\n", split_str[i]);
    }


    return 0;
}


char **split(char *str, char **delimiters, int number_of_delimiters, int *number_of_rows_in_return_array)
{
    //temporary storage for array to be returned
    char temp_store[100][200];
    int row = 0;//row size of array that will be returned

    char **split_str;

    int i, j, k, l, mark = 0;
    char temp[100];

    for (i = 0 ; str[i] != '\0' ; ++i)
    {

        //Iterating through all delimiters to check if any is str
        for (j = 0 ; j < number_of_delimiters ; ++j )
        {
            l = i;

            for (k = 0 ; delimiters[j][k] != '\0' ; ++k)
            {
                if (str[i] != delimiters[j][k])
                {
                    break;
                }

                ++l;
            }

            //This means delimiter is in string
            if (delimiters[j][k] == '\0')
            {
                //store the string before delimiter
                strcpy(temp_store[row], &str[mark]);
                temp_store[row ++][i - mark] = '\0';

                //store string after delimiter
                strcpy(temp_store[row], &str[i]);
                temp_store[row ++][k] = '\0';

                //mark index where this delimiter ended
                mark = l;

                //Set i to where delimiter ends and break so that outermost loop
                //can iterate from where delimiter ends
                i = l - 1;

                break;

            }

        }
    }

    //store the string remaining
    strcpy(temp_store[row++], &str[mark]);

    //Allocate the split_str and store temp_store into it
    split_str = (char **)malloc(row * sizeof(char *));
    for (i=0 ; i < row; i++)
    {
        split_str[i] = (char *)malloc(200 * sizeof(char));
        strcpy(split_str[i], temp_store[i]);
    }

    *number_of_rows_in_return_array = row;

    return split_str;

}
This should probably work. Note that I have passed int * number_of_rows_in_return_array by ref because we need to know the row size of the retuned array.

0 commentaires

1
votes

Tout d'abord, vous avez une erreur de mémoire ici:

#include <stdlib.h>
#include <string.h>

char *get_delimiters(char *str, char **delims)
{
  for (int i = 0; delims[i]; i++)
    if (!strncmp(str, delims[i], strlen(delims[i])))
      return delims[i];
  return NULL;
}

char **split(char *str, char **delimiters)
{
  char *string = strdup(str);
  char **result = NULL;
  int n = 0;
  char *delim = NULL;

  for (int i = 0; string[i]; i++)
    if (get_delimiters(string + i, delimiters))
      n++;
  result = malloc((n * 2 + 2) * sizeof(char *));
  if (!result)
    return NULL;
  result[0] = string;
  n = 1;
  for (int i = 0; string[i]; i++) {
    delim = get_delimiters(string + i, delimiters);
    if (delim) {
      string[i] = '\0';
      result[n++] = delim;
      result[n++] = string + i + strlen(delim);
    }
  }
  result[n] = NULL;
  return result;
}

Si str est NULL, la chaîne n'est pas initialisée et vous utilisez une valeur non initialisée en comparaison. p>

si vous voulez copier une chaîne, vous devez utiliser la fonction strdup , le = copiera simplement le pointeur et non le contenu du pointeur.


Et voici une façon de le faire:

static char *string;

if (str != NULL)
    string = str;
if (string == NULL)
    return string;

résultat:

[0] fichier 'ls>'
[1] "&&"
[2] "toto"
[3] «||»
[4] "bar"

souvenez-vous que le résultat et la chaîne sont malléables, vous devez donc libérer le résultat et le résultat [0]


3 commentaires

Merci pour l'aide ! Votre fonction a un comportement étrange lorsque deimiter est répété: "foo &&& bar" -> [foo] [&&] [& bar] [&&] [bar] < code> "foo &&&& bar" -> [foo] [&&] [] [&&] [& bar] [&&] [bar] Mais je peux contourner ce problème en prétraitant le entrée utilisateur donc ce n'est pas un problème ^^


Juste une question: en supposant que result est le retour de split , je dois libérer le result ou le result et < code> résultat [i] (pour i ++ jusqu'au résultat [i] == NULL)?


vous devez libérer chaque variable allouée avec malloc , vous devez donc libérer result ET result [0] (car strdup utilisez malloc ).



0
votes

Je suis entré dans l'abstraction. J'ai d'abord créé une bibliothèque "phrase", qui permet de manipuler une liste de chaînes terminée par NULL ( char * ). J'ai écrit quelques accesseurs initiaux ( phrase_init , phrase_size , phrase_free , phrase_add_str etc.).

Ensuite, je suis allé à split , la sorcière devient alors vraiment, vraiment facile - si un délimiteur est trouvé, ajoutez la chaîne du délimiteur à la phrase et ajoutez le délimiteur à la phrase. Puis incrémentez la position du pointeur de chaîne. Si le délimiteur n'est pas trouvé, ajoutez la chaîne restante à la phrase.

Il y a un réel problème avec les pointeurs doubles, car char ** n'est pas implicitement convertible en const char ** . Pour le code de production, j'essaierais probablement de refactoriser le code et d'essayer de prendre en compte la correction de const .

'ls > file' '&&' 'foo ' '||' ' bar'
cmp = 1

Le programme affichera:

#define _GNU_SOURCE 1
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <assert.h>
#include <stdbool.h>

/*
 * sentence - list of words
 */
/* ----------------------------------------------------------- */

// if this would be production code, I think I would go with a
// struct word_t { char *word; }; struct sentence_t { struct word_t *words; };
// Note: when sentence_add_* fail - they free *EVERYTHING*, so it doesn't work like realloc
// shared_ptr? Never heard of it.

char **sentence_init(void) {
    return NULL;
}

size_t sentence_size(char * const *t) {
    if (t == NULL) return 0;
    size_t i;
    for (i = 0; t[i] != NULL; ++i) {
        continue;
    }
    return i;
}

void sentence_free(char * const *t) {
    if (t == NULL) return;
    for (char * const *i = t; *i != NULL; ++i) {
        free(*i);
    }
    free((void*)t);
}

void sentence_printex(char * const *t, const char *fmt1, const char *delim, const char *end) {
    for (char * const *i = t; *i != NULL; ++i) {
        printf(fmt1, *i);
        if (*(i + 1) != NULL) {
            printf(delim);
        }
    }
    printf(end);
}

void sentence_print(char * const *t) {
    sentence_printex(t, "%s", " ", "\n");
}

void sentence_print_quote_words(char * const *t) {
    sentence_printex(t, "'%s'", " ", "\n");
}

bool sentence_cmp_const(const char * const *t, const char * const *other) {
    const char * const *t_i = t;
    const char * const *o_i = other;
    while (*t_i != NULL && o_i != NULL) {
        if (strcmp(*t_i, *o_i) != 0) {
            return false;
        }
        ++t_i;
        ++o_i;
    }
    return *t_i == NULL && *o_i == NULL;
}

// thet's always funny, because "dupa" in my language means "as*"
char **sentence_add_strdupped(char **t, char *strdupped) {
    const size_t n = sentence_size(t);
    const size_t add = 1 + 1;
    const size_t new_n = n + add;
    void * const pnt = realloc(t,  new_n * sizeof(char*));
    if (pnt == NULL) goto REALLOC_FAIL;

    // we have to have place for terminating NULL pointer
    assert(new_n >= 2);
    t = pnt;
    t[new_n - 2] = strdupped;
    t[new_n - 1] = NULL;

    // ownership of str goes to t
    return t;

    // ownership of str stays in the caller
    REALLOC_FAIL:
    sentence_free(t);
    return NULL;
}

char **sentence_add_strlened(char **t, const char *str, size_t len) {
    char *strdupped = malloc(len + 1);
    if (strdupped == NULL) goto MALLOC_FAIL;

    memcpy(strdupped, str, len);
    strdupped[len] = '\0';

    t = sentence_add_strdupped(t, strdupped);
    if (t == NULL) goto SENTENCE_ADD_STRDUPPED_FAIL;

    return t;

    SENTENCE_ADD_STRDUPPED_FAIL:
    free(strdupped);
    MALLOC_FAIL:
    sentence_free(t);
    return NULL;
}

char **sentence_add_str(char **t, const char *str) {
    const size_t str_len = strlen(str);
    return sentence_add_strlened(t, str, str_len);
}

/* ----------------------------------------------------------- */

/**
 * Puff. Run strstr for each of the elements inside NULL delimeters dellist.
 * If any returns not NULL, return the pointer as returned by strstr
 * And fill dellist_found with the pointer inside dellist (can be NULL).
 * Finally! A 3 star award is mine!
 */
char *str_find_any_strings(const char *str,
        const char * const *dellist,
        const char * const * *dellist_found) {
    assert(str != NULL);
    assert(dellist != NULL);
    for (const char * const *i = &dellist[0]; *i != NULL; ++i) {

        const char *found = strstr(str, *i);
        if (found != NULL) {
            if (dellist_found != NULL) {
                *dellist_found = i;
            }
            // __UNCONST(found)
            return (char*)found;
        }

    }
    return NULL;
}

/**
 * Split the string str according to the list od delimeters dellist
 * @param str
 * @param dellist
 * @return returns a dictionary
 */
char **split(const char *str, const char * const *dellist) {
    assert(str != NULL);
    assert(dellist != NULL);

    char **sen = sentence_init();

    while (*str != '\0') {
        const char * const *del_pnt = NULL;
        const char *found = str_find_any_strings(str, dellist, &del_pnt);
        if (found == NULL) {
            // we don't want an empty string to be the last...
            if (*str != '\0') {
                sen = sentence_add_str(sen, str);
                if (sen == NULL) return NULL;
            }
            break;
        }

        // Puff, so a delimeter is found at &str[found - str]
        const size_t idx = found - str;
        sen = sentence_add_strlened(sen, str, idx);
        if (sen == NULL) return NULL;

        assert(del_pnt != NULL);
        const char *del = *del_pnt;
        assert(del != NULL);
        assert(*del != '\0');
        const size_t del_len = strlen(del);
        sen = sentence_add_strlened(sen, del, del_len);
        if (sen == NULL) return NULL;

        str += idx + del_len;
    }

    return sen;
}

int main()
{
    char **sentence = split("ls > file&&foo || bar", (const char*[]){"&&", "||", NULL});
    assert(sentence != NULL);
    sentence_print_quote_words(sentence);
    printf("cmp = %d\n", sentence_cmp_const((void*)sentence, (const char*[]){"ls > file", "&&", "foo ", "||", " bar", NULL}));
    sentence_free(sentence);

    return 0;
}


0 commentaires