【问题标题】:String tokenizer without using strtok()不使用 strtok() 的字符串标记器
【发布时间】:2014-09-09 19:45:43
【问题描述】:

我正在编写一个不使用 strtok() 的字符串标记器。这主要是为了我自己的改进和对指针的更多理解。我想我几乎拥有它,但我一直收到以下错误:

myToc.c:25 warning: assignment makes integer from pointer without a cast
myToc.c:35 (same as above)
myToc.c:44 error: invalid type argument of 'unary *' (have 'int')

我正在做的是遍历发送到方法的字符串,找到每个分隔符,并将其替换为“\0”。 “ptr”数组应该有指向分离子串的指针。这是我目前所拥有的。

#include <string.h>

void myToc(char * str){
   int spcCount = 0;
   int ptrIndex = 0;

   int n = strlen(str);

   for(int i = 0; i < n; i++){
      if(i != 0 && str[i] == ' ' && str[i-1] != ' '){
         spcCount++;
      }
   }

   //Pointer array; +1 for \0 character, +1 for one word more than number of spaces
   int *ptr = (int *) calloc(spcCount+2, sizeof(char));
   ptr[spcCount+1] = '\0';
   //Used to differentiate separating spaces from unnecessary ones
   char temp;

   for(int j = 0; j < n; j++){
      if(j == 0){
         /*Line 25*/ ptr[ptrIndex] = &str[j];
         temp = str[j];
         ptrIndex++;
      }
      else{
         if(str[j] == ' '){
            temp = str[j];
            str[j] = '\0';
         }
         else if(str[j] != ' ' && str[j] != '\0' && temp == ' '){
            /*Line 35*/ ptr[ptrIndex] = &str[j];
            temp = str[j];
            ptrIndex++;
         }
      }
   }

   int k = 0;
   while(ptr[k] != '\0'){
      /*Line 44*/ printf("%s \n", *ptr[k]);
      k++;
   }
}

我可以看到错误发生在哪里,但我不确定如何纠正它们。我该怎么办?我是正确分配内存还是只是我如何指定地址的问题?

【问题讨论】:

    标签: c pointers tokenize


    【解决方案1】:

    你的指针数组是错误的。看起来像你想要的:

    char **ptr =  calloc(spcCount+2, sizeof(char*));
    

    另外,如果我正确阅读了您的代码,则不需要空字节,因为此数组不是字符串。

    此外,您还需要修复:

    while(ptr[k] != '\0'){
      /*Line 44*/ printf("%s \n", *ptr[k]);
      k++;
    }
    

    取消引用不是必需的,如果您删除 null ptr,这应该可以:

    for ( k = 0; k < ptrIndex; k++ ){
      /*Line 44*/ printf("%s \n", ptr[k]);
    }
    

    【讨论】:

    • 看起来这清除了我遇到的错误,但我只是尝试运行我的程序,但我遇到了段错误。用户输入一个字符串,然后由 myToc 函数拆分。
    【解决方案2】:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void myToc(char * str){
        int spcCount = 0;
        int ptrIndex = 0;
    
        int n = strlen(str);
    
        for(int i = 0; i < n; i++){
            if(i != 0 && str[i] == ' ' && str[i-1] != ' '){
                spcCount++;
            }
        }
    
        char **ptr = calloc(spcCount+2, sizeof(char*));
        //ptr[spcCount+1] = '\0';//0 initialized by calloc 
        char temp = ' ';//can simplify the code
    
        for(int j = 0; j < n; j++){
            if(str[j] == ' '){
                temp = str[j];
                str[j] = '\0';
            } else if(str[j] != '\0' && temp == ' '){//can omit `str[j] != ' ' &&`
                ptr[ptrIndex++] = &str[j];
                temp = str[j];
            }
        }
    
        int k = 0;
        while(ptr[k] != NULL){//better use NULL
            printf("%s \n", ptr[k++]);
        }
        free(ptr);
    }
    
    int main(){
        char test1[] = "a b c";
        myToc(test1);
        char test2[] = "hello world";
        myToc(test2);
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      更新:我在http://www.compileonline.com/compile_c99_online.php 试过这个 包含第 25、35 和 44 行的修复,以及调用的 main 函数 myToc() 两次。我最初在尝试写入空字符时遇到了段错误 到str[],但这只是因为我传递的字符串是(显然 不可修改)文字。当我分配一个文本缓冲区并在传递它们之前将字符串写入那里时,下面的代码可以正常工作。这个版本也可以修改为返回指针数组,然后指针数组将指向标记。

      (即使字符串参数不可修改,下面的代码也可以工作,只要 myToc() 制作字符串的本地副本;但如果函数的目的是返回令牌列表而不是打印它们,那将不会产生预期的效果。)

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      void myToc(char * str){
         int spcCount = 0;
         int ptrIndex = 0;
      
         int n = strlen(str);
      
         for(int i = 0; i < n; i++){
            if(i != 0 && str[i] == ' ' && str[i-1] != ' '){
               spcCount++;
            }
         }
      
         //Pointer array;  +1 for one word more than number of spaces
         char** ptr = (char**) calloc(spcCount+2, sizeof(char*));
         //Used to differentiate separating spaces from unnecessary ones
         char temp;
      
         for(int j = 0; j < n; j++){
            if(j == 0){
               ptr[ptrIndex] = &str[j];
               temp = str[j];
               ptrIndex++;
            }
            else{
               if(str[j] == ' '){
                  temp = str[j];
                  str[j] = '\0';
               }
               else if(str[j] != ' ' && str[j] != '\0' && temp == ' '){
                  ptr[ptrIndex] = &str[j];
                  temp = str[j];
                  ptrIndex++;
               }
            }
         }
      
         for (int k = 0; k < ptrIndex; ++k){
            printf("%s \n", ptr[k]);
         }
      }
      
      int main (int n, char** v)
      {
        char text[256];
        strcpy(text, "a b c");
        myToc(text);
        printf("-----\n");
        strcpy(text, "hello world");
        myToc(text);
      }
      

      不过,我更喜欢更简单的代码。基本上,您需要一个指向str[] 中第一个非空白字符的指针,然后是指向前面有一个空白的每个非空白(第一个除外)的指针。您的第一个循环几乎得到了这个想法,只是它正在寻找前面有非空白的空白。 (你也可以在i = 1 开始循环,避免每次迭代都测试i != 0。)

      我可能只是分配一个 char* 大小为 sizeof(char*) * (n + 1)/2 的数组来保存指针,而不是在字符串上循环两次(也就是说,我会省略第一个循环,它只是为了弄清楚数组)。无论如何,如果ptr[0] 是非空白的,我会将其地址写入数组;然后循环for (int j = 1; j &lt; n; ++j),如果str[j] 为非空白且str[j - 1] 为空白,则将str[j] 的地址写入数组——基本上你正在做的事情,但ifs 和辅助变量更少。 只要代码干净且有意义,更少的代码意味着更少的机会引入错误。

      前言:

      int *ptr = 声明了一个 int 数组。对于指向char 的指针数组,您需要

      char** ptr = (char**) calloc(spcCount+2, sizeof(char*));
      

      该行之前的评论似乎也表明了一些混乱。您的指针数组中没有终止 null,您不需要为一个指针分配空间,因此 spcCount+2 可能是 spcCount + 1

      这也是可疑的:

      while(ptr[k] != '\0')
      

      鉴于您使用calloc 的方式,它看起来会起作用(您确实需要spcCount+2 才能使这项工作),但我会觉得写这样的东西更安全:

      for (k = 0; k < ptrIndex; ++k)
      

      我不认为这是导致段错误的原因,只是让我有点不安将指针 (ptr[k]) 与 \0 进行比较(您通常会将其与 char 进行比较)。

      【讨论】:

      • 按照 dohashi 的指示修复第 44 行后,它是否仍然存在段错误?如果是这样,输入字符串是什么?
      • 它在你的修复和他的修复中都有段错误。我尝试的输入字符串是“a b c”和“hello world”。
      • 我刚刚意识到我没有实现完整的解决方案,特别是第 44 行建议的修复。我试过了,现在我没有收到任何段错误,但只有来自“a b c”的“a” " 被打印出来。
      • myToc.c 文件只包含 myToc 函数。主要功能在一个名为 tokenizer.c 的文件中。 myToc 在用户输入后被调用,并且应该返回一个指向指针数组的指针。我只是打印该方法中的所有内容进行测试。
      • 这提醒我,我不应该将不可修改的字符串传递给标记器函数。确保您传递的字符串始终可以被覆盖,并且我认为此处显示的函数将在不制作字符串的本地副本的情况下工作(然后返回指针数组将按需要工作,因为它将指向一个仍然存在于调用者中)。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-31
      • 1970-01-01
      相关资源
      最近更新 更多