C 无法使用memmem进行字符串搜索

C 无法使用memmem进行字符串搜索,c,C,我不喜欢把一堆代码放在这里,让别人帮我调试,但我对C语言有点缺乏经验,我完全被难倒了 总体目标是对一个非常大的日志文件(11G+)进行一些清理,我一次读取2048字节,然后扫描单独的行,将它们写入一个输出文件。我最初使用strstr查找行结尾,但是我发现这不适用于读取缓冲区末尾的部分行-我认为这是因为我从文件中读取的“字符串”末尾没有\0,strstr会被混淆 所以,在谷歌搜索了一段时间后,我想我应该试试memmem,它似乎是strstr的“二进制安全”替代品。这就是我被卡住的地方,我的程序在调

我不喜欢把一堆代码放在这里,让别人帮我调试,但我对C语言有点缺乏经验,我完全被难倒了

总体目标是对一个非常大的日志文件(11G+)进行一些清理,我一次读取2048字节,然后扫描单独的行,将它们写入一个输出文件。我最初使用strstr查找行结尾,但是我发现这不适用于读取缓冲区末尾的部分行-我认为这是因为我从文件中读取的“字符串”末尾没有\0,strstr会被混淆

所以,在谷歌搜索了一段时间后,我想我应该试试memmem,它似乎是strstr的“二进制安全”替代品。这就是我被卡住的地方,我的程序在调用memmem时出错了

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

#define BUFF_LEN 2048

int main (void)
{
    char file_buff[BUFF_LEN], prev_line[BUFF_LEN], curr_line[BUFF_LEN];
    char *p_line_start, *p_lf;
    int bytes_consumed, bytes_read;
    FILE *in_fp, *out_fp;

    in_fp = fopen("208.log", "r");
    out_fp = fopen("expanded.log", "w+");

    int sane = 0;
    while (1) {
        bytes_read = fread(file_buff, 1, BUFF_LEN, in_fp);
        if (bytes_read == 0) {
            break;
        }

        // Set the pointer to the beginning of the file buffer
        p_line_start = file_buff;
        bytes_consumed = 0;

        // Chomp lines
        while (bytes_consumed < bytes_read) {
            printf("Read to go with bytes_read = %d, bytes_consumed = %d\n",
                bytes_read, bytes_consumed);
            p_lf = (char *) memmem(p_line_start, bytes_read - bytes_consumed,
                "\n", 1);
            if (p_lf == NULL) {
                // No newline left in file_buff, store what's left in
                // curr_line and break out to read more from the file.
                printf("At loop exit I have chomped %ld of %d\n",
                    p_line_start - file_buff, bytes_read);
                //break;
                goto cleanup;
            }
            // Copy the line to our current line buffer (including the newline)
            memcpy(curr_line, p_line_start, p_lf - p_line_start + 1);
            printf("Chomped a line of length %ld\n", p_lf - p_line_start + 1);
            fwrite(curr_line, 1, p_lf - p_line_start + 1, out_fp);
            p_line_start = p_lf + 1;
            bytes_consumed += p_lf - p_line_start + 1;
        }
#包括
#包括
#定义BUFF_LEN 2048
内部主(空)
{
字符文件buff[buff\u LEN],上一行[buff\u LEN],当前行[buff\u LEN];
字符*p\u行\u开始,*p\u lf;
消耗的整数字节,读取的字节;
文件*in\u fp,*out\u fp;
in_fp=fopen(“208.log”,“r”);
out_fp=fopen(“expanded.log”,“w+”);
int sane=0;
而(1){
字节读取=fread(文件buff,1,buff,in fp);
如果(字节数_读取==0){
打破
}
//将指针设置为文件缓冲区的开头
p_line_start=file_buff;
消耗的字节数=0;
//咀嚼线
while(消耗的字节数<读取的字节数){
printf(“与字节一起读取\%d,消耗的字节\%d\n”,
字节(读取,消耗的字节);
p_lf=(char*)memmem(p_line_start,bytes_read-bytes_consumered,
“\n”,1);
if(p_lf==NULL){
//文件中没有换行符。\u buff,存储文件中剩余的内容
//curr_line和break out从文件中读取更多内容。
printf(“在循环出口处,我选择了%ld个%d\n”,
p_行_开始-文件_buff,字节_read);
//中断;
去清理;
}
//将行复制到当前行缓冲区(包括换行符)
memcpy(当前行、p\u行、p\u lf-p\u行、p\u开始+1);
printf(“选择了长度为%ld\n的行”,p\u lf-p\u line\u start+1);
写入(当前线路,1,左前-线路,起始+1,输出fp);
p_line_start=p_lf+1;
字节消耗+=p_lf-p_行开始+1;
}
谁能在这里给我一句台词?!

也欢迎提供如何更好地为自己调试的提示。

来自您的评论之一:

我正在强制转换返回值,因为gcc正在踢出警告: “警告:赋值从整数生成指针而不进行强制转换”

您只是通过强制转换返回值来隐藏问题

memmem返回一个指针。通常今天的指针是64位。如果您没有声明函数,编译器不知道它返回一个指针,而是假设它返回一个整数。通常今天的整数是32位。生成的代码将查找该整数返回的位置,并取3它实际得到的是返回指针的一半

尝试在调用memmem后添加这一行,看看如果您声明或不声明memmem,打印输出是否不同:

printf("[p_lf = %p]\n", (void*)p_lf);
当我使用您的原始程序(没有声明)运行它时,它打印了0xFFFFFFFFDA67,然后崩溃,因为这是一个无效的指针。使用声明(使用#define"GNU"SOURCE)时,它打印了0x7fffffffda67,但没有崩溃。 请注意,如果只取0x7fffffffda67的32个低位,则得到0xffffda67,然后将其扩展到64位,则得到0xFFFFFFFFDA67,即原始程序的指针。(地址空间布局随机化已关闭。)


这就是为什么您不应该强制转换返回值。

如果您可以使用
mmap
,您可能希望使用它将整个文件映射到内存中。这样,您就不必担心部分文件。(
mmap
延迟加载内存页,因此您也不必担心在11GB文件上耗尽RAM)。您是否尝试过在调试器中运行此程序?然后您可以确切地看到它崩溃的位置,并可以查看变量的值。您使用
fread
而不是
fgets
来读取文件,这会使事情变得过于复杂。当然,在使用
fgets
时,您可能仍然需要处理行截断问题。这段时间有多长输入文件中最长的一行?或者这是未知的?我希望文件I/O驱动程序正在读取大量的文件,因此您是否会看到
fread
fgets
之间的主要性能差异是值得怀疑的。至于最长的一行,使用5MB的行缓冲区(例如
char*buffer)并不是不合理的=malloc(5000000);while(fread(buffer,5000000,fp_in)!=NULL){…}
所以我想我的问题是,你认为日志文件的行数会超过5MB吗?@indiv:这是可能的,并且它可以使用至少与搜索字符串一样长的缓冲区(可能更短——不过,我的思维会从处理那个字符串开始收缩),但簿记开销巨大,容易出错,而且很难调试。我也会选择一个大的单行输入缓冲区。然后为了安全起见,将其大小增加一倍。有趣的是,man memmem指示定义
\u GNU SOURCE
,但事实上,只有定义了
\u USE\u GNU
时才声明该函数。符号高亮显示IDE中的提示和弹出式语法提示让用户误以为一切都很好,这让人更加困惑。