【问题标题】:Tokenize a line without strtok标记没有 strtok 的行
【发布时间】:2014-06-03 12:03:42
【问题描述】:

我正在从文件中读取行并对其进行标记。标记由空格分隔,或者是否在引号内(例如:"to ken")。

我写了一个代码,但是指针有问题。我不知道如何存储一行中的标记,或者更确切地说设置指向它们的指针。

还有人建议我在我“识别”的每个标记后面加上一个 0,这样我就会知道它何时结束,并且我在 char *tokens[] 中仅存储指向标记开头的指针。

我当前的代码:

char *tokens[50];
int token_count;

int tokenize(char *line){
    token_count = 0;
    int n = 0;          

    while(line[n] != NULL || line[n] != '\n'){
        while(isspace(line[n++]));
        if(line[n] == '"'){
            while(line[++n] != '"' || line[n] != NULL){
                  /* set tokens[n] */
            }
        }
        else{
            while(!isspace(line[n++])){
                  /*set tokens[n] */
            }

        }

        n++;
    }

    tokens[token_count] = 0;

}

【问题讨论】:

  • 'if(line[n] = '"'){' ... 应该是 '=='
  • line[n] != NULL 是错误的格式,应该是 line[n] != '\0'
  • 谢谢,会改的
  • 您的问题是什么?请修复我的代码?
  • @user2202368 \0 是终止 C 字符串的 NUL 字符 (0x00)。它实际上等于 NULL,但完全没有相同的含义。至于我的评论:如果你想指向 C 字符串中的位置,你只需要一个指向令牌第一个字符的指针,即 char*。

标签: c token tokenize


【解决方案1】:

您使用字符串基址line 和索引n 通过递增n 来逐步遍历字符串:

while (str[n] != '\0') n++;

如果你使用指针,你的任务可能会更容易:

while (*str != '\0') str++;

然后,您的标记可以在读取标记之前由指针的值表示,即当您点击引号或非空格时。这为您提供了令牌的开始。

令牌的长度呢?在 C 中,字符串是字符数组,以空字符结尾。这意味着,您的标记包含整行的其余部分,因此包含所有后续标记。您可以在每个标记之后放置一个'\0',但这有两个缺点:它不适用于只读字符串文字,并且根据您的标记语法,它并不总是可能的。例如,字符串a"b b"c 可能应该解析为三个标记a"b b"c,但是在标记之后放置空字符会破坏标记化过程。

另一种方法是将标记存储为指向起始字符和长度的指针对。这些标记不再以 null 结尾,因此如果要将它们与标准 C 字符串函数一起使用,则必须将它们写入临时缓冲区。

这是一种方法。

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

struct token {
    const char *str;
    int length;
};

int tokenize(const char *p, struct token tk[], int n)
{
    const char *start;
    int count = 0;   

    while (*p) {
        while (isspace(*p)) p++;
        if (*p == '\0') break;

        start = p;
        if (*p == '"') {
            p++;
            while (*p && *p != '"') p++;
            if (*p == '\0') return -1;        /* quote not closed */            
            p++;
        } else {            
            while (*p && !isspace(*p) && *p != '"') p++;
        }

        if (count < n) {
            tk[count].str = start;
            tk[count].length = p - start;
        }
        count++;
    }

    return count;
}

void token_print(const struct token tk[], int n)
{
    int i;

    for (i = 0; i < n; i++) {
        printf("[%d] '%.*s'\n", i, tk[i].length, tk[i].str);
    }
}

#define MAX_TOKEN 10

int main()
{
    const char *line = "The \"New York\" Stock Exchange";
    struct token tk[MAX_TOKEN];
    int n;

    n = tokenize(line, tk, MAX_TOKEN);
    if (n > MAX_TOKEN) n = MAX_TOKEN;
    token_print(tk, n);    

    return 0;
}

每个令牌的开头保存在一个局部变量中,并在扫描后分配给令牌。当p指向token后面的字符时,表达式:

p - start

给你长度。 (这称为指针运算。)例程扫描所有标记,但它最多只分配n 标记,以免溢出提供的缓冲区。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    • 2021-08-12
    • 1970-01-01
    • 2021-07-06
    • 1970-01-01
    相关资源
    最近更新 更多