使用不同的分隔符拆分文本

Split a text using different delimiters

很长时间没有用 c 编写代码,我有点麻烦。

我从格式如下的文件中获取 tex:

# 3 10
P1 16 3
P2 8  1
P3 10 2

从第二行开始,每一行都代表一个进程,该进程具有自己的属性除以 space,我需要获取每个属性并将它们放入像这样的结构数组中

typedef struct process
{
    char *name;
    int priority;
    int duration;
};

表示进程,有什么提示吗?

我尝试使用 strtok\n 作为分隔符来隔离行,然后在 while 的内部使用 strtok" " 作为分隔符第一个 strtok 但显然不起作用。

评论区已经指出,strtok的嵌套是不允许的,但是可以使用函数fgets获取单独的行,然后使用strtok标记这些行的内容。

如果你想只用strtok而不用fgets解决问题,那也是可以的。

在 POSIX(例如 Linux)系统上,您可以简单地使用 strtok_r 而不是 strtok,这允许嵌套。

在 non-POSIX 上,由于不允许嵌套,您首先必须执行一系列 strtok 调用以获取指向各行开头的指针,并且您必须记住所有这些指针。完成第一个 strtok 调用序列后,您可以使用 strtok 标记各个行的内容:

下面是这样一个 non-POSIX 解决方案的例子:

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

#define MAX_LINES 100

int main( void )
{
    char input[] =
        "# 3 10\n"
        "P1 16 3\n"
        "P2 8  1\n"
        "P3 10 2\n";

    char *lines[MAX_LINES];
    int num_lines;
    char *p;

    //perform the first sequence of strtok
    p = strtok( input, "\n" );
    for ( num_lines = 0; p != NULL; num_lines++ )
    {
        if ( num_lines == MAX_LINES )
        {
            fprintf( stderr, "Too many lines!\n" );
            exit( EXIT_FAILURE );
        }

        //remember line
        lines[num_lines] = p;

        p = strtok( NULL, "\n" );
    }

    //perform additional sequence of strtok for every line
    for ( int i = 0; i < num_lines; i++ )
    {
        p = strtok( lines[i], " " );

        printf( "Line %d:\n", i + 1 );

        for ( int j = 0; p != NULL ; j++ )
        {
            printf( "Token #%d: %s\n", j + 1, p );
            p = strtok( NULL, " " );
        }

        printf( "\n" );
    }
}

这个程序有以下输出:

Line 1:
Token #1: #
Token #2: 3
Token #3: 10

Line 2:
Token #1: P1
Token #2: 16
Token #3: 3

Line 3:
Token #1: P2
Token #2: 8
Token #3: 1

Line 4:
Token #1: P3
Token #2: 10
Token #3: 2

如何将单个标记写入 struct process 是一个完全不同的问题。

为了将单个标记的字符串转换为数字,您可以使用函数strtol。之后,您可以将这些数字写给个人 struct 成员。

为了写入struct processname成员,可以使用函数strdup, if your platform already supports it. Otherwise, you will have to malloc sufficient space for the copy of the string, and then use strcpy。如果该输入缓冲区未用于其他用途并且保证该缓冲区的生命周期至少与 struct process 的生命周期一样长,您也可以直接将 name 指向输入缓冲区内目的。在那种情况下,没有必要复制字符串。

要逐行读取输入,首先使用fgets(3)函数:

while (fgets(line, sizeof line, f)) {
    ....
}

这将允许您逐行读取文件并在读取时处理每一行。

其次,strtok(3)可以完美使用,如:

char *name = strtok(line, delim),
     *prio = strtok(NULL, delim),
     *dur  = strtok(NULL, delim);

这将允许您识别空行(如果 nameNULL)、注释行(name'#' 字符开头)或缺失的字段(行其中 priodurNULL)

最后,使用strdup(3)为名称字符串分配内存,因为我们将为整行使用一个固定的line缓冲区,它将被重用,一旦我们去下一条记录,所以我们需要显式分配(我们推迟到最后一个操作,所以在没有 space 或格式错误的情况下,我们没有为这个槽分配内存)

最后,代码是这样的:

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

struct process {
    char *name;
    int priority;
    int duration;
};

#define MAX_PROC 100
struct process processes[MAX_PROC];

ssize_t read_processes(FILE *f, struct process *proc, size_t count);
void print_process(FILE *out, struct process *proc);

#define E_NOSPACE       (-1)
#define E_BAD_FORMAT    (-2)
#define E_MALLOC        (-3)

ssize_t read_processes(FILE *f, struct process *proc, size_t count)
{
    char        line[256];          /* temporary storage */
    const char *delim    = " \n\t"; /* delimiter string for strtok */
    size_t      ret_val  = 0;
    int         line_num = 0;       /* line number */

    while (fgets(line, sizeof line, f)) {
        line_num++;

        char *name = strtok(line, delim),
             *prio = strtok(NULL, delim),
             *dur  = strtok(NULL, delim);

        if (!name || name[0] == '#')
            continue; /* empty line or comment */

        if (!prio || !dur)
            return E_BAD_FORMAT;

        if (count <= 0) /* no space left in array */
            return E_NOSPACE;

        if (sscanf(prio, "%d", &proc->priority) != 1)
            return E_BAD_FORMAT;

        /* we do malloc last, so we don't need to free()
         * the allocated memory in case of bad format. */
        if ((proc->name = strdup(name)) == NULL)
            return E_MALLOC;

        proc++;
        count--;
        ret_val++;
    }
    return ret_val;
}

int main()
{
    ssize_t n = read_processes(stdin, processes, MAX_PROC);
    if (n < 0) {
        char *s = "<<OTHER ERROR>> (this shouldn't happen)";
        switch(n) {
        case E_BAD_FORMAT: s = "bad format in string"; break;
        case E_NOSPACE: s = "no space in array"; break;
        case E_MALLOC: s = "cannot allocate memory"; break;
        }
        fprintf(stderr, "Error: %s\n", s);
        exit(1);
    }
    struct process *p, *end = processes + n;

    for (p = processes; p < end; p++) {
        print_process(stdout, p);
    }
}

void print_process(FILE *out, struct process *proc)
{
    fprintf(out, "Process name: %s\n", proc->name);
    fprintf(out, "    priority: %d\n", proc->priority);
    fprintf(out, "    duration: %d\n", proc->duration);
}

并且当 运行 时,它会产生以下输出:

$ a.out <<EOF
# This is a comment
# This is another comment
# below is an empty line.

P1 2 3
P6 4 5
EOF
Process name: P1
    priority: 2
    duration: 3
Process name: P6
    priority: 4
    duration: 5
$ _