您在词"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.
查看一下,如果您有任何问题,请告诉我。