【问题标题】:How to get words out of a string and put them in an string array ? In C如何从字符串中取出单词并将它们放入字符串数组中?在 C 中
【发布时间】:2018-09-02 16:31:19
【问题描述】:

我基本上在一个字符串中有一个句子,并且想将它分解为每个单词。每个单词都应该进入一个字符串数组。我不允许使用strtok。我有这个代码,但它不起作用。有人可以帮忙吗?

互联网上肯定有类似的东西,但我找不到任何东西......

int main(){

    char s[10000];                        // sentence
    char array[100][100];                 // array where I put every word

    printf("Insert sentence: ");          // receive the sentence
    gets(s);

    int i = 0;
    int j = 0;

    for(j = 0; s[j] != '\0'; j++){        // loop until I reach the end
        for(i = 0; s[i] != ' '; i++){     // loop until the word is over
            array[j][i] = s[i];           // put every char in the array
        }
    }

    return 0;
}

【问题讨论】:

  • 什么不起作用?
  • 我的 gets() 文档说:Never use gets().
  • 正如我所说,练习是分解 s。当我打印出数组时,我得到的第一个单词是我插入的第一个单词的 4 倍。可以说这些论点是:你好,我是汤姆。结果我收到了这个:hi hi hi hi hi
  • 对糟糕的英语表示遗憾。
  • @Ttomas - 不! strlen 通过寻找终止的 '0' 来工作

标签: c arrays string search multidimensional-array


【解决方案1】:

每个单词都应该放入一个字符串数组中。我不允许使用 strtok.

可以用紧凑算法解决的有趣问题。 它处理check(char c)中指定的多个空格和标点符号。

问题中最困难的部分是正确处理极端情况。我们可能会遇到单词长度超过WORD_LEN长度或单词数超过array容量的情况。

这两种情况都得到妥善处理。该算法会截断多余的单词并仅解析到数组的容量。

(顺便说一句。不要使用getsWhy is the gets function so dangerous that it should not be used?

编辑:已提供经过全面测试的find_tokens 函数。

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

#define WORD_LEN            3 // 100 // MAX WORD LEN
#define NR_OF_WORDS         3 // 100 // MAX NUMBER OF WORDS
#define INPUT_SIZE 10000

int is_delimiter(const char * delimiters, char c) // check for a delimiter
{
    char *p = strchr (delimiters, c);    // if not NULL c is separator

    if (p) return 1;                     // delimeter
    else return 0;                       // not a delimeter
}    

int skip(int *i, char *str, int skip_delimiters, const char *delimiters)
{
    while(1){
        if(skip_delimiters) {
            if(  (str[(*i)+1] =='\0') || (!is_delimiter(delimiters, str[(*i)+1])) )  
                break;        // break on nondelimeter or '\0' 
            else (*i)++;      // advance to next character
        } 
        else{                 // skip excess characters in the token
            if( is_delimiter(delimiters, str[(*i)]) ) 
            {
                if(  (str[(*i)+1] =='\0') || !is_delimiter(delimiters, str[(*i)+1]) )
                    break;    // break on non delimiter or '\0'
                else (*i)++;  // skip delimiters
            }
            else (*i)++;      // skip non delimiters               
        }
    }        

    if ( str[(*i)+1] =='\0') return 0;
    else return 1;
}                

int find_tokens(int max_tokens, int token_len, char *str, char array[][token_len+1], const char *delimiters, int *nr_of_tokens)
{
    int i =  0;
    int j =  0;
    int l =  0;
    *nr_of_tokens = 0;
    int status = 0;                           // all OK!
    int skip_leading_delimiters = 1;
    int token = 0;
    int more;

    for(i = 0; str[i] != '\0'; i++){          // loop until I reach the end

        // skip leading delimiters
        if( skip_leading_delimiters )
        {
           if( is_delimiter( delimiters, str[i]) ) continue;
           skip_leading_delimiters = 0;
        }

        if( !is_delimiter(delimiters,str[i]) && (j < token_len) )          
        {
            array[l][j] = str[i];             // put  char in the array
            //printf("%c!\n", array[l][j] );
            j++;
            array[l][j] = 0;
            token = 1;
        }
        else
        {   
            //printf("%c?\n", str[i] );
            array[l][j] = '\0';                        // token terminations

            if (j < token_len) {
               more = skip(&i, str, 1, delimiters);    // skip delimiters
            }
            else{
                more = skip(&i, str, 0, delimiters);  // skip excess of the characters in token
                status = status | 0x01;               // token has been truncated
            }

            j = 0;
            //printf("more %d\n",more);
            if(token){
                if (more) l++;
            }

            if(l >= max_tokens){
                status = status | 0x02;              // more tokens than expected
                break;
            }
        }
    }

    if(l>=max_tokens)
        *nr_of_tokens = max_tokens;
    else{
        if(l<=0 && token)
          *nr_of_tokens = 1;
        else
        {
            if(token)
                *nr_of_tokens = l+1;
            else   
                *nr_of_tokens = l;
        }
    }
    return status;
}    

int main(void){
    char input[INPUT_SIZE+1];                // sentence
    char array[NR_OF_WORDS][WORD_LEN+1];     // array where I put every word, remeber to include null terminator!!!

    int number_of_words;
    const char * delimiters =  " .,;:\t";    // word delimiters 
    char *p;

    printf("Insert sentence: ");             // receive the sentence
    fgets(input, INPUT_SIZE, stdin);
    if ( (p = strchr(input, '\n')) != NULL) *p = '\0'; // remove '\n'

    int ret = find_tokens(NR_OF_WORDS, WORD_LEN, input, array, delimiters, &number_of_words);

    printf("tokens= %d ret= %d\n", number_of_words, ret);

    for (int i=0; i < number_of_words; i++)
        printf("%d: %s\n", i, array[i]);

    printf("End\n");    
    return 0;
}

测试:

Insert sentence: ..........1234567,,,,,,abcdefgh....123::::::::::::                                                                          
tokens= 3 ret= 1                                                                                                                             
0: 123                                                                                                                                       
1: abc                                                                                                                                       
2: 123                                                                                                                                       
End

【讨论】:

    【解决方案2】:

    你不是'\0'-终止字符串,你正在扫描源 每次你找到一个空字符时的开始。

    你只需要一个循环,内循环和条件必须是s[i] != 0

    int j = 0; // index for array
    int k = 0; // index for array[j]
    for(i = 0; s[i] != '\0'; ++i)
    {
        if(k == 99)
        {
            // word longer than array[j] can hold, aborting
            array[j][99] = 0; // 0-terminating string
            break;
        }
    
        if(j == 99)
        {
            // more words than array can hold, aborting
            break;
        }
    
        if(s[i] == ' ')
        {
            array[j][k] = 0; // 0-terminating string
            j++; // for the next entry in array
            k = 0;
        } else
            array[j][k++] = s[i]; 
    }
    

    请注意,此算法不处理多个空格和标点符号。 这可以通过使用存储最后状态的变量来解决。

    int j = 0; // index for array
    int k = 0; // index for array[j]
    int sep_state = 0; // 0 normal mode, 1 separation mode
    for(i = 0; s[i] != '\0'; ++i)
    {
        if(k == 99)
        {
            // word longer than array[j] can hold, aborting
            array[j][99] = 0; // 0-terminating string
            break;
        }
    
        if(j == 99)
        {
            // more words than array can hold, aborting
            break;
        }
    
        // check for usual word separators
        if(s[i] == ' ' || s[i] == '.' || s[i] == ',' || s[i] == ';' || s[i] == ':')
        {
            if(sep_state == 1)
                continue; // skip multiple separators
            array[j][k] = 0; // 0-terminating string
            j++; // for the next entry in array
            k = 0;
            sep_state = 1; // enter separation mode
        } else {
            array[j][k++] = s[i];
            sep_state = 0; // leave separation mode
        }
    }
    

    如您所见,使用 sep_state 变量我可以检查是否有多个 分隔符一个接一个地出现并跳过后续的分隔符。我也 检查常见的标点符号。

    【讨论】:

    • OP 说他不允许使用 strtok。
    • 不处理多个空格。
    • @bruceg 对,我错过了 OP 说他/她不能使用 strtok 的部分。从答案中删除它
    • @MFisherKDX 分配没有说明多个空格,但你是对的,它不处理它。我会修复答案。
    【解决方案3】:
    #include <stdio.h>
    
    int main()
    {
    
    char s[10000];                        // sentence
    char array[100][100];                 // array where i put every word
    
    printf("Insert sentence: ");          // receive the sentece
    gets(s);
    printf("%s",s);
    
    int i = 0;
    int j = 0;
    int k = 0;
    
    for(j = 0; s[j] != '\0'; j++){        // loop until i reach the end
    
      if ( s[j] != ' ' || s[j] == '\0' )
      {
        array[i][k] = s[j];
        k++;
      }
      else {
        i++;
        k = 0;
      }
    
    }
    
    return 0;
    }
    

    请注意,gets 函数非常不安全,在任何情况下都不应该使用,请改用 scanf 或 fgets

    【讨论】:

    • 检查s[j] == '\0'是不必要的,循环的中断条件是s[j] != '\0',所以s[j]在循环中永远不会是'\0'
    • 不幸的是,由于@Pablo 所述的原因,该算法不起作用。另外 1) 它允许缓冲区溢出:不检查单词的长度 2) 单词的数量可以超过数组容量 3) 它不处理多个分隔符。 4)它不能处理多个空格或标点符号。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-01
    • 1970-01-01
    相关资源
    最近更新 更多