c - 不使用 strtok 标记一行

标签 c token tokenize

我正在从文件中读取行并对它们进行标记。 token 通过空格分隔或位于引号内进行区分(例如:“to ken”)。

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

另外,有人建议我在我“识别”的每个标记后面放一个 0,这样我就知道它何时结束,并且我仅将指向 a 的指针存储在 char *tokens[] 中 token 的开始。

我当前的代码:

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;

}

最佳答案

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

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

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

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

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

token 的长度怎么样?在 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;
}

每个 token 的开头都保存在本地变量中,并在扫描后分配给该 token 。当p指向token后面的字符时,表达式:

p - start

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

关于c - 不使用 strtok 标记一行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24014715/

相关文章:

c - 为什么需要 int 类型来处理 EOF 和 getchar() 的返回?

java - 如何在java中的引号之间获取数据?

python - 为什么 gensim 的 simple_preprocess Python 分词器似乎跳过了 "i"分词?

c - 如果文件未以 sudo 打开,则系统调用 open C 无法打印文件内容

c - 未初始化字符数组的 %d 说明符

c - fgets() 读取的字符少于预期

authentication - 如何使用用户名和密码获取 github token

javascript - 使用 google/token 添加更多时间,能够在一天中的任何时间获取 cal 事件?

azure - 如何从 Azure AD token 获取用户详细信息?

python - 如何标记正则表达式模式本身?