如何在C语言中使用正则表达式查找文件中的某行?

5

我该如何在C编程中使用正则表达式?例如,如果我想在文件中查找一行。

DAEMONS=(sysklogd network sshd !netfs !crond)

然后,像这样在单独的一行中打印每个守护进程。
sysklogd 
network 
sshd 
!netfs 
!crond

这是我目前所做的:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <regex.h>
#define tofind    "[a-z A-Z] $"
int main(){
 FILE *fp;
 char line[1024];
 int retval = 0;
 char address[256];
 regex_t re;

 if(regcomp(&re, tofind, REG_EXTENDED) != 0)
  return;

 fp = fopen("/etc/rc.conf","r");//this file has this line "DAEMONS=(sysklogd network sshd !netfs !crond)"
 while((fgets(line, 1024, fp)) != NULL) {
     if((retval = regexec(&re, address, 0, NULL, 0)) == 0)
      printf("%s\n", address);
 } 
}

任何帮助都将不胜感激。
1个回答

6
您将文本读入到line中,因此您应该将line传递给regexec()。您还需要考虑行末的换行符是否会影响模式匹配。(使用fgets()是正确的,但请记住它会保留行末的换行符。)
您还应该执行return -1;(或任何不是0模256的其他值),而不是一个普通的没有返回值的return。此外,您应该检查文件是否已打开;我必须使用替代名称,因为在我的机器上 - MacOS X中没有/etc/rc.conf这样的文件。
以下是适用于我个人情况的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <regex.h>

#define tofind    "[a-z A-Z] $"

int main(int argc, char **argv)
{
    FILE *fp;
    char line[1024];
    int retval = 0;
    regex_t re;
    //this file has this line "DAEMONS=(sysklogd network sshd !netfs !crond)"
    const char *filename = "/etc/rc.conf";

    if (argc > 1)
        filename = argv[1];

    if (regcomp(&re, tofind, REG_EXTENDED) != 0)
    {
        fprintf(stderr, "Failed to compile regex '%s'\n", tofind);
        return EXIT_FAILURE;
    }

    fp = fopen(filename, "r");
    if (fp == 0)
    {
        fprintf(stderr, "Failed to open file %s (%d: %s)\n",
                filename, errno, strerror(errno));
        return EXIT_FAILURE;
    }

    while ((fgets(line, 1024, fp)) != NULL)
    {
        line[strlen(line)-1] = '\0';
        if ((retval = regexec(&re, line, 0, NULL, 0)) == 0)
            printf("<<%s>>\n", line);
    } 
    return EXIT_SUCCESS;
}

如果您需要帮助编写正则表达式而不是用它们编写C代码的帮助,那么我们需要设计正则表达式以匹配您展示的行。
^DAEMONS=([^)]*) *$

这将匹配与示例相同的行。如果您可以在“S”和“=”之间或者“=”和“(”之间有空格,则需要进行适当的修改。我已经允许有尾随空白符 - 人们常常马虎;但是如果他们使用尾随制表符,那么该行将不被选中。
找到该行后,您需要将其分成几部分。您可以选择使用'捕获'括号功能,或者简单地使用strchr()查找开放括号,然后使用适当的技术来分离守护程序名称 - 我会避免使用strtok(),可能会使用strspn()strcspn()来查找单词。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <regex.h>

#define tofind    "^DAEMONS=\\(([^)]*)\\)[ \t]*$"

int main(int argc, char **argv)
{
    FILE *fp;
    char line[1024];
    int retval = 0;
    regex_t re;
    regmatch_t rm[2];
    //this file has this line "DAEMONS=(sysklogd network sshd !netfs !crond)"
    const char *filename = "/etc/rc.conf";

    if (argc > 1)
        filename = argv[1];

    if (regcomp(&re, tofind, REG_EXTENDED) != 0)
    {
        fprintf(stderr, "Failed to compile regex '%s'\n", tofind);
        return EXIT_FAILURE;
    }

    fp = fopen(filename, "r");
    if (fp == 0)
    {
        fprintf(stderr, "Failed to open file %s (%d: %s)\n", filename, errno, strerror(errno));
        return EXIT_FAILURE;
    }

    while ((fgets(line, 1024, fp)) != NULL)
    {
        line[strlen(line)-1] = '\0';
        if ((retval = regexec(&re, line, 2, rm, 0)) == 0)
        {
            printf("<<%s>>\n", line);
            printf("Line: <<%.*s>>\n", (int)(rm[0].rm_eo - rm[0].rm_so), line + rm[0].rm_so);
            printf("Text: <<%.*s>>\n", (int)(rm[1].rm_eo - rm[1].rm_so), line + rm[1].rm_so);
            char *src = line + rm[1].rm_so;
            char *end = line + rm[1].rm_eo;
            while (src < end)
            {
                size_t len = strcspn(src, " ");
                if (src + len > end)
                    len = end - src;
                printf("Name: <<%.*s>>\n", (int)len, src);
                src += len;
                src += strspn(src, " ");
            }
        }
    }
    return EXIT_SUCCESS;
}

这里有很多调试代码,但是你不需要花费太长时间就能得到你想要的答案。我得到的结果是:

<<DAEMONS=(sysklogd network sshd !netfs !crond)>>
Line: <<DAEMONS=(sysklogd network sshd !netfs !crond)>>
Text: <<sysklogd network sshd !netfs !crond>>
Name: <<sysklogd>>
Name: <<network>>
Name: <<sshd>>
Name: <<!netfs>>
Name: <<!crond>>

注意:当你想在正则表达式中使用反斜杠时,你需要在C源代码中写两个反斜杠。


1
只是想补充一下,正则表达式“toFind”有缺陷。无论如何都不会匹配到任何内容。 - Jeff Mercado
@Jeff:你确定吗?正如所示,该RE查找单个字母后跟空格和行尾或两个空格和行尾。无论这是否适用于解析/etc/rc.conf都是一个问题-但是,该RE确实找到了它想要找到的内容。 - Jonathan Leffler
仍然没有得到任何输出?/etc/rc.conf 的内容是 <code> 嘟嘟嘟 DAEMONS=(sysklogd network sshd !netfs !crond) 嘟嘟嘟 </code> - Face
@Face:好的 - 那么正则表达式不是你想要的。你需要在问题中展示一个示例行(缩进为代码),并在其下方展示你想要正则表达式查找的部分。正如我在之前的评论中解释的那样,你输入的正则表达式查找以字母字符或空格开头,后跟一行末尾的空格。如果这不是你要找的内容,请解释你要找什么。这将塑造你实际需要的正则表达式。 - Jonathan Leffler
@Jonatha:如果我表达不清楚很抱歉。我正在尝试在“DAEMONS =(.....)”之间分离每个单词,并将每个单词打印在新行中。 - Face

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接