0

私がこれを持っていると仮定します:

"foo bar 1 and foo bar 2"

どうすれば分割できますか:

foo bar 1
foo bar 2

試しstrtok()ましstrsep()たが、どちらも機能しませんでした。「and」を区切り文字として認識せず、「a」、「n」、および「d」を区切り文字として認識します。

これを支援する関数はありますか、それとも空白で分割して文字列操作を行う必要がありますか?

4

4 に答える 4

5

strstr()を使用して、最初の「」と「」を検索し、非常に多くの文字をスキップして再度実行するだけで、文字列を自分で「トークン化」できます。

于 2010-03-28T01:46:27.300 に答える
5

Cで文字列を分割する際の主な問題は、必然的に動的なメモリ管理が行われることであり、可能な限り標準ライブラリによって回避される傾向があります。そのため、標準のC関数はいずれも動的メモリ割り当てを処理せず、malloc / calloc/reallocのみが処理します。

しかし、これを自分で行うことはそれほど難しくありません。それについて説明させてください。

いくつかの文字列を返す必要があります。これを行う最も簡単な方法は、文字列へのポインタの配列を返すことです。これは、NULL項目で終了します。最後のNULLを除いて、配列内の各要素は動的に割り当てられた文字列を指します。

まず、このような配列を処理するためのヘルパー関数がいくつか必要です。最も簡単なのは、文字列(最後のNULLの前の要素)の数を計算するものです。

/* Return length of a NULL-delimited array of strings. */
size_t str_array_len(char **array)
{
    size_t len;

    for (len = 0; array[len] != NULL; ++len)
        continue;
    return len;
}

もう1つの簡単な方法は、配列を解放するための関数です。

/* Free a dynamic array of dynamic strings. */
void str_array_free(char **array)
{
    if (array == NULL)
        return;
    for (size_t i = 0; array[i] != NULL; ++i)
        free(array[i]);
    free(array);
}

もう少し複雑なのは、文字列のコピーを配列に追加する関数です。配列がまだ存在しない場合(配列全体がNULLである場合)など、いくつかの特殊なケースを処理する必要があります。また、「\ 0」で終了していない文字列を処理する必要があります。これにより、実際の分割関数で、追加時に入力文字列の一部だけを使用しやすくなります。

/* Append an item to a dynamically allocated array of strings. On failure,
   return NULL, in which case the original array is intact. The item
   string is dynamically copied. If the array is NULL, allocate a new
   array. Otherwise, extend the array. Make sure the array is always
   NULL-terminated. Input string might not be '\0'-terminated. */
char **str_array_append(char **array, size_t nitems, const char *item, 
                        size_t itemlen)
{
    /* Make a dynamic copy of the item. */
    char *copy;
    if (item == NULL)
        copy = NULL;
    else {
        copy = malloc(itemlen + 1);
        if (copy == NULL)
            return NULL;
        memcpy(copy, item, itemlen);
        copy[itemlen] = '\0';
    }

    /* Extend array with one element. Except extend it by two elements, 
       in case it did not yet exist. This might mean it is a teeny bit
       too big, but we don't care. */
    array = realloc(array, (nitems + 2) * sizeof(array[0]));
    if (array == NULL) {
        free(copy);
        return NULL;
    }

    /* Add copy of item to array, and return it. */
    array[nitems] = copy;
    array[nitems+1] = NULL;
    return array;
}

それは一口です。本当に良いスタイルの場合、入力項目がそれ自体の機能である場合は、動的コピーの作成を分割する方がよいでしょうが、それは読者の練習問題として残しておきます。

最後に、実際の分割関数があります。また、いくつかの特殊なケースを処理する必要があります。

  • 入力文字列は、区切り文字で開始または終了する場合があります。
  • セパレータが隣り合っている場合があります。
  • 入力文字列にセパレータがまったく含まれていない可能性があります。

セパレータが入力文字列の開始または終了のすぐ隣にある場合、または別のセパレータのすぐ隣にある場合は、結果に空の文字列を追加することを選択しました。他に何か必要な場合は、コードを微調整する必要があります。

特殊なケースといくつかのエラー処理を除けば、分割はかなり簡単になりました。

/* Split a string into substrings. Return dynamic array of dynamically
   allocated substrings, or NULL if there was an error. Caller is
   expected to free the memory, for example with str_array_free. */
char **str_split(const char *input, const char *sep)
{
    size_t nitems = 0;
    char **array = NULL;
    const char *start = input;
    char *next = strstr(start, sep);
    size_t seplen = strlen(sep);
    const char *item;
    size_t itemlen;

    for (;;) {
        next = strstr(start, sep);
        if (next == NULL) {
            /* Add the remaining string (or empty string, if input ends with
               separator. */
            char **new = str_array_append(array, nitems, start, strlen(start));
            if (new == NULL) {
                str_array_free(array);
                return NULL;
            }
            array = new;
            ++nitems;
            break;
        } else if (next == input) {
            /* Input starts with separator. */
            item = "";
            itemlen = 0;
        } else {
            item = start;
            itemlen = next - item;
        }
        char **new = str_array_append(array, nitems, item, itemlen);
        if (new == NULL) {
            str_array_free(array);
            return NULL;
        }
        array = new;
        ++nitems;
        start = next + seplen;
    }

    if (nitems == 0) {
        /* Input does not contain separator at all. */
        assert(array == NULL);
        array = str_array_append(array, nitems, input, strlen(input));
    }

    return array;
}

Here is the whole program in one piece. It also includes a main program to run some test cases.

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


/* Append an item to a dynamically allocated array of strings. On failure,
   return NULL, in which case the original array is intact. The item
   string is dynamically copied. If the array is NULL, allocate a new
   array. Otherwise, extend the array. Make sure the array is always
   NULL-terminated. Input string might not be '\0'-terminated. */
char **str_array_append(char **array, size_t nitems, const char *item, 
                        size_t itemlen)
{
    /* Make a dynamic copy of the item. */
    char *copy;
    if (item == NULL)
        copy = NULL;
    else {
        copy = malloc(itemlen + 1);
        if (copy == NULL)
            return NULL;
        memcpy(copy, item, itemlen);
        copy[itemlen] = '\0';
    }

    /* Extend array with one element. Except extend it by two elements, 
       in case it did not yet exist. This might mean it is a teeny bit
       too big, but we don't care. */
    array = realloc(array, (nitems + 2) * sizeof(array[0]));
    if (array == NULL) {
        free(copy);
        return NULL;
    }

    /* Add copy of item to array, and return it. */
    array[nitems] = copy;
    array[nitems+1] = NULL;
    return array;
}


/* Free a dynamic array of dynamic strings. */
void str_array_free(char **array)
{
    if (array == NULL)
        return;
    for (size_t i = 0; array[i] != NULL; ++i)
        free(array[i]);
    free(array);
}


/* Split a string into substrings. Return dynamic array of dynamically
   allocated substrings, or NULL if there was an error. Caller is
   expected to free the memory, for example with str_array_free. */
char **str_split(const char *input, const char *sep)
{
    size_t nitems = 0;
    char **array = NULL;
    const char *start = input;
    char *next = strstr(start, sep);
    size_t seplen = strlen(sep);
    const char *item;
    size_t itemlen;

    for (;;) {
        next = strstr(start, sep);
        if (next == NULL) {
            /* Add the remaining string (or empty string, if input ends with
               separator. */
            char **new = str_array_append(array, nitems, start, strlen(start));
            if (new == NULL) {
                str_array_free(array);
                return NULL;
            }
            array = new;
            ++nitems;
            break;
        } else if (next == input) {
            /* Input starts with separator. */
            item = "";
            itemlen = 0;
        } else {
            item = start;
            itemlen = next - item;
        }
        char **new = str_array_append(array, nitems, item, itemlen);
        if (new == NULL) {
            str_array_free(array);
            return NULL;
        }
        array = new;
        ++nitems;
        start = next + seplen;
    }

    if (nitems == 0) {
        /* Input does not contain separator at all. */
        assert(array == NULL);
        array = str_array_append(array, nitems, input, strlen(input));
    }

    return array;
}


/* Return length of a NULL-delimited array of strings. */
size_t str_array_len(char **array)
{
    size_t len;

    for (len = 0; array[len] != NULL; ++len)
        continue;
    return len;
}


#define MAX_OUTPUT 20


int main(void)
{
    struct {
        const char *input;
        const char *sep;
        char *output[MAX_OUTPUT];
    } tab[] = {
        /* Input is empty string. Output should be a list with an empty 
           string. */
        {
            "",
            "and",
            {
                "",
                NULL,
            },
        },
        /* Input is exactly the separator. Output should be two empty 
           strings. */
        {
            "and",
            "and",
            {
                "",
                "",
                NULL,
            },
        },
        /* Input is non-empty, but does not have separator. Output should
           be the same string. */
        {
            "foo",
            "and",
            {
                "foo",
                NULL,
            },
        },
        /* Input is non-empty, and does have separator. */
        {
            "foo bar 1 and foo bar 2",
            " and ",
            {
                "foo bar 1",
                "foo bar 2",
                NULL,
            },
        },
    };
    const int tab_len = sizeof(tab) / sizeof(tab[0]);
    bool errors;

    errors = false;

    for (int i = 0; i < tab_len; ++i) {
        printf("test %d\n", i);

        char **output = str_split(tab[i].input, tab[i].sep);
        if (output == NULL) {
            fprintf(stderr, "output is NULL\n");
            errors = true;
            break;
        }
        size_t num_output = str_array_len(output);
        printf("num_output %lu\n", (unsigned long) num_output);

        size_t num_correct = str_array_len(tab[i].output);
        if (num_output != num_correct) {
            fprintf(stderr, "wrong number of outputs (%lu, not %lu)\n",
                    (unsigned long) num_output, (unsigned long) num_correct);
            errors = true;
        } else {
            for (size_t j = 0; j < num_output; ++j) {
                if (strcmp(tab[i].output[j], output[j]) != 0) {
                    fprintf(stderr, "output[%lu] is '%s' not '%s'\n",
                            (unsigned long) j, output[j], tab[i].output[j]);
                    errors = true;
                    break;
                }
            }
        }

        str_array_free(output);
        printf("\n");
    }

    if (errors)
        return EXIT_FAILURE;   
    return 0;
}
于 2010-03-28T03:03:57.740 に答える
2

strstrこれは、特定の文字列で文字列を分割するために使用する方法を示した、私が今書いた素敵な短い例です。

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

void split(char *phrase, char *delimiter)
{
    char *loc = strstr(phrase, delimiter);
    if (loc == NULL)
    {
        printf("Could not find delimiter\n");
    }
    else
    {
        char buf[256]; /* malloc would be much more robust here */
        int length = strlen(delimiter);
        strncpy(buf, phrase, loc - phrase);
        printf("Before delimiter: '%s'\n", buf);
        printf("After delimiter: '%s'\n", loc+length);
    }
}

int main()
{
    split("foo bar 1 and foo bar 2", "and");
    printf("-----\n");
    split("foo bar 1 and foo bar 2", "quux");
    return 0;
}

出力:

区切り文字の前:'foo bar 1'
区切り文字の後:'foo bar 2'
-----
区切り文字が見つかりませんでした

もちろん、私はそれを完全にテストしていません、そしてそれはおそらく文字列の長さに関連する標準的なバッファオーバーフローの問題のほとんどに対して脆弱です。しかし、それは少なくとも実証可能な例です。

于 2010-03-28T01:52:31.140 に答える
0

If you know the type of delimiter example comma or semicolon you can try with this:

#include<stdio.h>
#include<conio.h>
int main()
{
  int i=0,temp=0,temp1=0, temp2=0;
  char buff[12]="123;456;789";
   for(i=0;buff[i]!=';',i++)
   {
     temp=temp*10+(buff[i]-48);
   }
   for(i=0;buff[i]!=';',i++)
   {
     temp1=temp1*10+(buff[i]-48);
   }
   for(i=0;buff[i],i++)
   {
     temp2=temp2*10+(buff[i]-48);
   }
    printf("temp=%d temp1=%d temp2=%d",temp,temp1,temp2);
    getch();
  return 0;
}

Output:

temp=123 temp1=456 temp2=789
于 2014-02-04T20:32:32.223 に答える