【问题标题】:C file to multiple char *groups by word delimiterC 文件到多个 char *groups by word delimiter
【发布时间】:2018-11-26 23:40:55
【问题描述】:

我有一个内容类似于以下的文件:

Really my data is here, and I think its really 
cool. Somewhere, i want to break on some really
awesome data. Please let me really explain what is going
'\n'
on. You are amazing. Something is really awesome. 
Please give me the stuffs. 

我想创建一个数组,其中包含指向分隔词之间的字符串的字符串指针。

char **字符串:

my data is here, and I think its
cool. Somewhere, i want to break on some
awesome data. Please let me
explain what is going'\n'on. You are amazing. Something is
awesome.'\n'Please give me the stuffs.

尝试的代码:

char *filedata = malloc(fileLength);
fread(filedata, end, 1, fp); //ABC
size_t stringCount = 8;
size_t idx = 0;
char **data = malloc(stringCount * sizeof(*packets));
if(!data) {
    fprintf(stderr, "There was an error");
    return 1;
}
fread(data, end, 1, text);
char *stuff = strtok(data, "really");
while(stuff) {
    data[idx++] = strdup(stuff);
    s = strtok(NULL, "stuff");
    if(idx >= stringCount) {
        stringCount *= 2;
        void *tmp = realloc(stuff, stringCount * sizeof(*stuff));
        if(!tmp) {
            perror("Unable to make a larger string list");
            stringCount /= 2;
            break;
        }
        stuff = tmp;
    }
}

这提供了一些我正在寻找的东西,但它并不界定单词本身而不是字母。

【问题讨论】:

  • 我会使用strstr,因为这是在较长字符串中查找单词的最快方法。找到单词后,您需要检查单词前后的字符,以确保该单词不是较长单词的子字符串。例如,如果单词是“on”,那么strstr会在“once”“wonder”和“国家”,这可能不是你想要的。
  • 就执行而言,它不是“最快的”,但就编写工作程序而言,它可能是“最快的”,对于第一次阅读代码的人来说,它可能是“最快的”弄清楚程序做了什么。
  • 如果你不喜欢strstr(),你可以调查regcomp()regexec()
  • 没有最好的方法。这取决于数据的性质。见How does grep run so fast?
  • 你试过什么?您发布的算法看起来可以工作。

标签: c string strsplit strstr


【解决方案1】:

您在词"really" 上标记“文件” 的目标存在一些微妙的困难。这些是什么?文本文件通常一次读取一行,如果存储整个文件的行,则作为多个指针,每个指针指向一行的开头。这意味着,如果采用一般的面向行的 方法来读取文件,则您的标记(从文件开头开始,或以单词"really" 开头)可能跨越多行。因此,要进行标记化,您需要组合多行。

或者,您可以将整个文件读入单个缓冲区,然后使用strstr 解析分隔符"really"但是...,您需要确保缓冲区保存该文件是 nul-terminated 以避免最终调用 strstr 的未定义行为。 (通常将整个文件读入缓冲区不会导致 nul-terminated 缓冲区)

也就是说,即使使用strstr,您也必须有效地手动解析文件的内容。您将需要保留三指针(一个指向标记开头的开始指针,一个用于搜索分隔符的指针,以处理找到的分隔符是一个较大单词的较少包含的子字符串 ,最后是一个结束指针,用于标记令牌的结束。

该方案相当简单,您的第一个标记开始和文件的开头,并且每个后续标记都以单词"really" 开头。所以你向前扫描找到" really"(注意" really"之前的空格),将结束指针设置为你的token的开头" really",将token复制到一个缓冲区,/* do stuff with token */free (token);,更新你的开始指向"really" 开头的指针,将您的一般解析指针设置为过去的"really" 并重复直到找不到"really"。当您退出解析循环时,您仍然需要使用最终令牌 /* do stuff */

您还可以决定如何处理每个令牌中包含的'\n'。出于以下输出目的,它们仅被' ' 覆盖。 (您可以添加您喜欢的任何其他标准,例如消除由换行符替换引起的任何尾随或中间空格,这留给您)

总而言之,您可以执行类似于以下的操作,其中将文件内容读取到 nul-terminated 缓冲区由函数 read_file() 处理,其余的标记化是简单处理在main(),例如

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

char *read_file (char* fname, size_t *nbytes)
{
    long bytes = 0;
    char* file_content;
    FILE *file = fopen(fname, "rb");

    if (!file)          /* validate file open for reading */
        return NULL;

    fseek (file, 0, SEEK_END);              /* fseek end of file */
    if ((bytes = ftell (file)) == -1) {     /* get number of bytes */
        fprintf (stderr, "error: unable to determine file length.\n");
        return NULL;
    }

    fseek (file, 0, SEEK_SET);              /* fseek beginning of file */

    /* allocate memory for file */
    if (!(file_content = malloc (bytes + 1))) { /* allocate/validate memory */
        perror ("malloc - virtual memory exhausted");
        return NULL;
    }

    /* read all data into file in single call to fread */
    if (fread (file_content, 1, (size_t)bytes, file) != (size_t)bytes) {
        fprintf (stderr, "error: failed to read %ld-bytes from '%s'.\n",
                bytes, fname);
        return NULL;
    }
    fclose (file);              /* close file */

    file_content[bytes] = 0;    /* nul terminate - to allow strstr use */

    *nbytes = (size_t)bytes;    /* update nbytes making size avialable */

    return file_content;        /* return pointer to caller */
}

int main (int argc, char **argv) {

    size_t nbytes;
    char *content;

    if (argc < 2) {     /* validate required argument givent */
        fprintf (stderr, "error: insufficient input. filename req'd.\n");
        return 1;
    }

    if ((content = read_file (argv[1], &nbytes))) { /* read/validate */
        char *sp = content,     /* start pointer for token */
            *p = sp,            /* pointer for parsing token */
            *ep = p;            /* end pointer one past end of token */
        const char *delim = " really";      /* delimiter */

        while ((ep = strstr (p, delim))) {  /* while delimiter found */
            if (isspace (*(ep + sizeof delim - 1)) ||   /* if next isspace */
                ispunct (*(ep + sizeof delim - 1))) {   /* or next ispunct */
                /* delimiter found */
                size_t tlen = ep - sp;              /* get token length */
                char *token = malloc (tlen + 1),    /* allocate for token */
                    *tp = token;                    /* pointer to token */
                if (!token) {                       /* validate allocation */
                    perror ("malloc-token");
                    exit (EXIT_FAILURE);
                }
                memcpy (token, sp, tlen);           /* copy to token */
                *(token + tlen) = 0;                /* nul-termiante */
                while (*tp) {               /* replace '\n' with ' ' */
                    if (*tp == '\n')
                        *tp = ' ';
                    tp++;
                }
                printf ("\ntoken: %s\n", token);    /* output token */
                /* do stuff with token */
                free (token);                       /* free token memory */
                sp = ep + 1;    /* advance start to beginning of next token */
            }
            p = ep + sizeof delim;  /* advance pointer */
        }
        p = sp;             /* use p to change '\n' to ' ' in last token */
        while (*p) {        /* replacement loop */
            if (*p == '\n')
                *p = ' ';
            p++;
        }
        printf ("\ntoken: %s\n", sp);
        /* do stuff with last token */

        free (content);     /* free buffer holding file */
    }

    return 0;
}

输入文件示例

$ cat dat/breakreally.txt
my data is here, and I think its really
cool. Somewhere, i want to break on some really
awesome data. Please let me really explain what is going
on. You are amazing.

使用/输出示例

$ ./bin/freadbreakreally dat/breakreally.txt

token: my data is here, and I think its

token: really  cool. Somewhere, i want to break on some

token: really awesome data. Please let me

token: really explain what is going on. You are amazing.

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

【讨论】:

  • 非常感谢您的解释以及示例代码。使用两者,我能够推导出完成工作的必要方法。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-03
  • 2022-11-20
相关资源
最近更新 更多