【问题标题】:String tokenization strange character output字符串标记化奇怪字符输出
【发布时间】:2016-08-14 03:30:57
【问题描述】:

我正在尝试标记一个字符串数组,但是,我的程序不断打印这些奇怪的字符。我相信这与我的字符串以空值结尾有关。如果是这个问题,我该怎么做才能解决它?

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

int main(void)
{
    char* s[] = { "12, 34, 56, 78", "82.16, 41.296",
                  "2, -3, 5, -7, 11, -13, 17, -19",
                  "9.00009, 90.0009, 900.009, 9000.09, 90000.9" };

    char *token = strtok(s, ", ");

    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, ", ");
    }
    return 0;
}

这是输出的照片。

谢谢

【问题讨论】:

  • 查看警告。修复this

标签: c token


【解决方案1】:

您要么搞砸了s 的声明(很可能给定了其余代码),要么搞砸了声明 s 并在 s 上调用 strtok 的方式(这是一个 array-of-pointers-to-char* 包含指向 字符串字面量 的指针,如所写。)

看来您实际上想要char s[] 作为您的声明。这将揭示初始化中存在几个缺失 几个无关 ','s 的问题。要将 s 声明为 array-of-char 初始化并保存 comma-separated-values 列表,您基本上想要

char s[] = { "12, 34, 56, 78, ....,  9000.09, 90000.9" };

在初始化时不要求您只有一组引号 (".."),但您试图从字符串中标记化的每个值都必须有一个 逗号 后面(除了最后一个值)。您可以如下声明和初始化s

char s[] = { "12, 34, 56, 78," "82.16, 41.296,"
              "2, -3, 5, -7, 11, -13, 17, -19,"
              "9.00009, 90.0009, 900.009, 9000.09, 90000.9" };

您的代码的其余部分在这种情况下工作正常,产生以下输出:

$ ./bin/strtok_arr
12
34
56
78
82.16
41.296
2
-3
5
-7
11
-13
17
-19
9.00009
90.0009
900.009
9000.09
90000.9

如果您的意图是创建一个 array-of-pointers-to-char*(例如 char *s[]),那么您必须重新编写声明和代码的其余部分,因为 (1 ) 您没有将字符指针传递给strtok;和 (2) strtok 修改传递给 strtok 的字符串,同时传递一个完全错误的 字符串文字 - 并保证 SegFault

如果您有任何问题,请告诉我。


作为指向字符的指针数组*

根据您的评论,如果您需要找到s 中每个单独字符串的meanaverage,那么s 必须是一个指向字符的指针数组*。正如评论中解释的那样,您不能初始化 char *s[] 以包含 { "stuff", "morestuff", ... },因为 "stuff""morestuff"字符串文字,并且在大多数情况下将在 只读中创建em>内存。由于strtok 修改了原始字符串,您将尝试修改 只读 内存,10 次中有 9 次会导致友好的 分段错误(不好) .

但是,您可以简单地将单个字符串创建为 字符数组,然后从字符数组创建 s,例如:

    char s1[] = "12, 34, 56, 78",
         s2[] = "82.16, 41.296",
         s3[] = "2, -3, 5, -7, 11, -13, 17, -19",
         s4[] = "9.00009, 90.0009, 900.009, 9000.09, 90000.9",
         *s[] = { s1, s2, s3, s4 };

然后,您可以通过使用strtok 标记每个字符串并将每个值转换为double 来完成您的代码,同时收集每个字符串的sumaverage。例如

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

int main (void)
{
    char s1[] = "12, 34, 56, 78",
         s2[] = "82.16, 41.296",
         s3[] = "2, -3, 5, -7, 11, -13, 17, -19",
         s4[] = "9.00009, 90.0009, 900.009, 9000.09, 90000.9",
         *s[] = { s1, s2, s3, s4 };
    size_t i, idx = 0, n = sizeof s/sizeof *s;
    double avg[n];

    for (i = 0; i < n; i++) {

        double sum = 0.0;
        size_t nval = 0;
        char *token = strtok (s[i], ", ");

        while (token != NULL) {
            sum += strtod (token, NULL);
            nval++;
            printf ("  %8s, sum : %9.2lf\n", token, sum);
            token = strtok (NULL, ", ");
        }
        printf ("----------------------------\n");
        printf ("        average : %9.2lf\n\n", (avg[idx++] = sum/nval));
    }

    return 0;
}

我可能会将标记化循环重写为 for 循环,以在循环定义本身中包含 nval 增量,例如

        for (; token; token = strtok (NULL, ", "), nval++) {
            sum += strtod (token, NULL);
            printf ("  %8s, sum : %9.2lf\n", token, sum);
        }

无论哪种情况,每个字符串的sumaverage 将如下所示:

$  ./bin/strtok_arr1
        12, sum :     12.00
        34, sum :     46.00
        56, sum :    102.00
        78, sum :    180.00
----------------------------
        average :     45.00

     82.16, sum :     82.16
    41.296, sum :    123.46
----------------------------
        average :     61.73

         2, sum :      2.00
        -3, sum :     -1.00
         5, sum :      4.00
        -7, sum :     -3.00
        11, sum :      8.00
       -13, sum :     -5.00
        17, sum :     12.00
       -19, sum :     -7.00
----------------------------
        average :     -0.88

   9.00009, sum :      9.00
   90.0009, sum :     99.00
   900.009, sum :    999.01
   9000.09, sum :   9999.10
   90000.9, sum : 100000.00
----------------------------
        average :  20000.00

查看一下,如果您有任何其他问题,请告诉我。

【讨论】:

  • 效果很好,打印效果也不错。我正在尝试编写一个程序,然后获取这些标记化的字符串,将它们转换为双精度,然后找到每一行的平均值。在我的讲师的解决方案中,他使用 char* = s[..] 并用逗号分隔数组中的每个字符串;所以这就是为什么我认为我也需要这样做。我仍然不确定他为什么这样做或他是如何做到的。谢谢你的帮助! :)
  • 如果您需要每个 row 的平均值,并且您认为 row 是,例如"12, 34, 56, 78",那么您需要char *s[],以便您可以将每一行传递给strtokstrsep。但是,您不能像最初那样声明 s,因为 "stuff" 包含 within { "stuff", "morestuff", ...} 是在 只读中创建的字符串文字 > .rodata 部分的内存 (Linux)。由于strtokstrsep 修改了原始字符串,将只读字符串传递给strtok 将导致分段错误 或(segfault)。我会换一种方法,另请参阅 cdlane 的 答案。
  • 您的编辑帮助很大!感谢您的帮助和时间。
【解决方案2】:

strtok() 接受一个指向字符数组的指针(在这里我将其称为“字符串”),但您传递给它的是一个字符串数组

此外,strtok()修改您传入的字符串,将分隔符替换为空字符。

您传递给strtok() 的字符串数组由指向数组中各个字符串的指针组成。所以显示乱码是这些指针显示为字符串的结果。此外,当strtok() 修改你给它的“字符串”时,这可能会导致各种内存损坏。

【讨论】:

    【解决方案3】:

    为了预测 BLUEPIXY 在 Nuchy 的解决方案中存在的两个问题,以下代码将常量字符串复制到用户分配的内存中,以便在 Unix 上修改它们而不会出现 BUS ERROR。

    下面使用更新的可重入strsep() 而不是strtok()

    ", ",如果传递给strsep(),与原始代码不同,不会中断,并删除逗号和空格的组合,它会中断两者。但仅使用 "," 会在数据上留下不需要的空间,我将单独删除。

    最后,我重新整理了数据以明确输入字符串有四个,而不是三个,并计算了字符串的数量,而不是硬编码计数:

    #include <ctype.h>
    #include <stdio.h>
    #include <string.h>
    
    #define BUFFER_SIZE (1024)
    
    int main(int argc, char *argv[]) {
    
        char strings[][BUFFER_SIZE] = {
            "12, 34, 56, 78",
            "82.16, 41.296",
            "2, -3, 5, -7, 11, -13, 17, -19",
            "9.00009, 90.0009, 900.009, 9000.09, 90000.9"
        };
    
        size_t limit = sizeof(strings) / BUFFER_SIZE;
    
        for (size_t i = 0; i < limit; i++) {
            char *token, *string = strings[i];
    
            while ((token = strsep(&string, ",")) != NULL) {
                while (isspace(*token)) {
                    token++;
                }
                printf("%s\n", token);
            }
        }
    
        return 0;
    }
    

    【讨论】:

    • 哈!您甚至可以使用strsep 来防止出现空白字段,干得好。但是,如果我是一个打赌的人(我不是),我愿意打赌,鉴于处理 指向 char* 的指针数组 所需的额外复杂性,OP 可能他的问题在于s 的声明而不是他的代码的其余部分。但这只是一个有根据的猜测:)
    • @DavidC.Rankin,我想我应该下这个赌注!
    • 是的,我会掏腰包来支付:)(这是一个很好的例子,说明为什么我不是一个在包含我无法控制的不确定性的事情上下注的人)。
    【解决方案4】:

    您需要单独标记每个字符串 - strtok() 函数接受指向 char 的指针作为其第一个参数:

    char *strtok(char * str, const char * delim);
    

    类似:

    #define _CRT_SECURE_NO_WARNINGS
    
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    
    int main(void)
    {
        char* s[] = { "12, 34, 56, 78", "82.16, 41.296",
                      "2, -3, 5, -7, 11, -13, 17, -19",
                      "9.00009, 90.0009, 900.009, 9000.09, 90000.9" };
        int sNo = 0;
    
        while (sNo < 4) {
            char *token = strtok(s[sNo++], ", ");
    
            while (token != NULL) {
                printf("%s\n", token);
                token = strtok(NULL, ", ");
            }
        }
    
        return 0;
    }
    

    当然,这需要您提前知道数组的大小。

    【讨论】:

    • 这有两个问题。
    • s 中有 3 个条目时为什么是 4?此外,您正在修改字符串文字,如果您迁移到基于 Unix 的机器,而文字存在于只读内存中,这肯定会让您的代码头疼。
    • s中有4个单独的字符串。不过你是对的 - strtok 修改了输入字符串,这会导致问题。
    猜你喜欢
    • 2021-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-26
    • 2020-07-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多