我正在研究一个程序,该程序接收一个文件作为输入,读取该文件,然后计算每个唯一单词出现的次数。我需要将每个单词沿任何非字母字符分开。例如,aren't
将变成两个单独的单词aren
和t
。我将如何去做呢?我目前正在这样做:
char* test = strtok(buffer, " 1234567890.,':;/\"?!@#$%^&*()'\0'\n");
while (test != NULL){
BST->root = addToBST(test, BST->root);
test = strtok(NULL, " 1234567890.,':;/\"?!@#$%^&*()'\0'\n");
}
但是,这似乎效率很低,我敢肯定有更好的方法可以做到这一点。有什么想法吗?
答案 0 :(得分:2)
一种可能性是使用对strspn
and strcspn
的交替调用来找到每个单词和每个单词间序列的长度。一旦知道子字符串的开始和长度,就可以使用strndup
创建动态分配的副本,或者可以使用strncmp
与已知长度的存储单词进行比较。
不一定是最快的解决方案。如您所注意到的,使用一长串定界符(或非定界符)需要进行大量的字符测试或布尔向量的构造,这对于可能是几个字符的字符串来说,会带来很多开销。不过,它相当快,开发人员的时间也很重要。
我上面列出了所有上述部分内容,以表明如果您准备冒险超越K&R字符串功能,那么现代C在很大程度上已经超出了原始字符串库设计的局限性及其对NUL终止的依赖。 。但是,实际上,我可能会在Flex中编写此代码,它可以有效地处理大多数缓冲区管理,并使用预先计算好的特里(或状态机)进行扫描,通常比您要执行的任何操作都要快不费吹灰之力就可以在一起。 YMMV。祝你好运。
答案 1 :(得分:0)
您能否利用ASCII字母在十进制数字序列中最连续的事实(az:65-90,AZ:97-122)来减少比较次数,方法是基于以下内容构建自己的strtok版本:这个一般概念:
void tokenizer(char* target) {
int i = 0;
char test = target[0];
while (test != '\0') {
if ((test >= 65 && test <= 90) || (test >= 97 && test <= 122))
printf("%c", test);
else {
printf("\n");
}
++i;
test = target[i];
}
}
然后也许将每个单词收集到缓冲区中,通过哈希函数运行它以获取唯一标识符,然后将其作为结构插入二进制搜索树中(按哈希值,单词名称和计数排序)以计算重复项独特的单词?
答案 2 :(得分:0)
使用strspn()
和strcspn()
函数与使用标准C库一样好,但是如果反复重复,编写出比其他C语言更出色的代码并不难搜索相同的字符集,这是常见的模式。
创建一个函数set_span()
,该函数将256个字节的数组设置为所有字节为零,然后将字符串中列出的那些字符设置为字符串(这将是strspn()
或strcspn()
的搜索参数)到1。
然后创建函数str_span()
和str_cspan()
,这些函数采用初始化数组之一和要搜索的字符串。它们可以非常快速地检查字符串中的每个字符是否被计数。请注意,名称str_span()
和str_cspan()
不是C11标准(§7.31.13)保留的。
此代码在我的GitHub上的SOQ(堆栈溢出问题)存储库中,作为packages子目录中的文件strspan-1.03.tgz
提供。库文件为strspan.h
和strspan.c
;其他文件提供测试支持代码。没有代码可以将strspan.o
放入库中。 (该软件包假定您使用的是GCC;如果您不使用GCC,则不难为其他编译器修改makefile
。)
我在一个小文件(great.panjandrum
)和一个大文件(bible-be.txt
–基本英语圣经)上进行了一些测试。处理每个文件3次的计时结果是:
$ test2.strspan great.panjandrum great.panjandrum great.panjandrum
# NB: The tests for str_span and strspn are comparable
# The tests for strlen and strchr are not comparable
strlen 0.000046 (487) great.panjandrum
strlen 0.000031 (487) great.panjandrum
strlen 0.000030 (487) great.panjandrum
strchr 0.000036 (487) great.panjandrum
strchr 0.000030 (487) great.panjandrum
strchr 0.000030 (487) great.panjandrum
str_span 0.000035 (487) great.panjandrum
str_span 0.000032 (487) great.panjandrum
str_span 0.000031 (487) great.panjandrum
strspn 0.000061 (487) great.panjandrum
strspn 0.000052 (487) great.panjandrum
strspn 0.000053 (487) great.panjandrum
$ test.strspan2 bible-be.txt bible-be.txt bible-be.txt
# NB: The tests for str_span and strspn are comparable
# The tests for strlen and strchr are not comparable
strlen 0.187297 (4467663) bible-be.txt
strlen 0.186324 (4467663) bible-be.txt
strlen 0.187616 (4467663) bible-be.txt
strchr 0.182676 (4467663) bible-be.txt
strchr 0.185405 (4467663) bible-be.txt
strchr 0.184813 (4467663) bible-be.txt
str_span 0.195715 (4467663) bible-be.txt
str_span 0.199516 (4467663) bible-be.txt
str_span 0.194588 (4467663) bible-be.txt
strspn 0.347890 (4467663) bible-be.txt
strspn 0.346028 (4467663) bible-be.txt
strspn 0.347305 (4467663) bible-be.txt
$
strlen()
和strchr()
测试的时间基本上衡量了读取和扫描文件的时间— strlen()
正在寻找一个空字节; strchr()
正在寻找换行符。首先运行它们意味着I / O系统在内存等中拥有文件。
str_span()
和strspn()
的时间表明str_span()
比strspan()
快得多。即使在数据少于0.5 KiB的文件上也可以测量;在包含约4.5 MiB数据的文件中,它非常引人注目。通过JFTR,该测试是在运行macOS Mojave 10.14.6(带有本地编译的GCC 9.3.0和Xcode 11.3.1)的2017 MacBook Pro上完成的。
这两个测试交替使用正匹配项和负匹配项。
请注意,设置函数set_span()
和set_ranges()
最初都将数组参数清零(实际上是一个包含数组的结构)。最好允许使用单独的set_zero()
函数来累积它们以重置结构(或者您可以使用memset()
-它会使用它)。