在C语言中提取子字符串

7
我正在尝试使用gcc在Linux上从此URI字段中提取用户名的ANSI C代码。
mail:username@example.com

我需要删除邮件地址中的"mail:"以及@符号之后的所有内容。在C语言中是否有内置函数可以提取子字符串?

4个回答

8
char *uri_field = "mail:username@example.com";

char username[64];

sscanf(uri_field, "mail:%63[^@]", username);

如果开头可能有其他“垃圾”(不一定只是mail:),则可以改为以下方式:
sscanf(uri_field, "%*[^:]:%63[^@]", username);

@user2284570:你的格式字符串没有意义。鉴于/etc/resolv.conf文件的正常格式,你需要像这样的字符串:fscanf(fp, "nameserver %20[^\n]", address); - Jerry Coffin
@JerryCoffin:它只在一些罕见的情况下工作(这是我尝试的第一件事),因为resolv.conf文件通常不以nameserver开头。大多数情况下,该文件以注释(“# Generated by NetworkManager”)或其他参数(如domainssearch)开头。 - user2284570
@user2284570:是的——对于这种情况,通常需要/希望读取一行,尝试将其扫描为您关心的类型的行,检查sscanf的返回值以查看是否成功,如果没有成功,则忽略该行并继续进行下一个。 - Jerry Coffin
@JerryCoffin。我不想将文件复制到缓冲区中(这就是为什么我想使用单个fscanf直接获取结果,而不是srtok)。我只需要一个单独的名称服务器。我需要确切的“正则表达式”(我知道它实际上不是正则表达式),它将意味着所有长度不确定的内容,直到“nameserver”。因为%*似乎不是正确的解决方案,或者我的表达式有误。 - user2284570
@JerryCoffin:在我的情况下,resolv.conf总是由第三方应用程序生成的。因此,我知道一行永远不会被注释掉(我的应用程序将在本地运行,不会公开)。那么你有关于“正则表达式”的任何想法吗?拜托了... - user2284570
显示剩余8条评论

3
你可以使用 strtok。看这个例子:
/* strtok example */
#include <stdio.h>
#include <string.h>

    int main ()
    {
      char str[] ="mail:username@example.com";
      char * pch;
      pch = strtok (str," :@");
      while (pch != NULL)
      {
        printf ("%s\n",pch);
        pch = strtok (NULL, " :@");
      }
      return 0;
    }

希望能帮到你。

0
void getEmailName(const char *email, char **name /* out */) {
    if (!name) {
        return;
    }

    const char *emailName = strchr(email, ':');

    if (emailName) {
        ++emailName;
    } else {
        emailName = email;
    }

    char *emailNameCopy = strdup(emailName);

    if (!emailNameCopy) {
        *name = NULL;

        return;
    }

    char *atSign = strchr(emailNameCopy, '@');

    if (atSign) {
        *atSign = '\0'; // To remove the '@'
        // atSign[1] = '\0';  // To keep the '@'
    }

    if (*name) {
        strcpy(*name, emailNameCopy);
    } else {
        *name = emailNameCopy;
    }
}

这将创建一个指向字符串中的:字符(冒号)的指针。 (它不会复制字符串。)如果找到:,则指向其后面的字符。 如果不存在:,则使用字符串的开头(即假定没有mail:前缀)。

现在我们想要从@开始剥离所有内容,因此我们复制字符串(emailNameCopy),然后稍后截断@

然后,代码将创建一个指向字符串中的@字符(atSign)的指针。 如果存在@字符(即strchr返回非NULL),则在@处的字符设置为零,标记字符串的结尾。 (不会创建新副本。)

然后我们返回字符串,或者如果给出了缓冲区,则复制它。


请注意,这是C99。我不确定strdup是否标准(我一直对这一点感到困惑),但阅读手册应该能告诉您它的作用,并且可以在几行代码中轻松复制它。 - strager
仅供参考,strdup 不是标准函数(虽然它非常普遍)。正式地说,这个名称是保留的,因此如果您自己编写一个 strdup 函数,就会导致未定义的行为。任何真正受到困扰的人都可以使用其他不以 str 开头的名称。 - Jerry Coffin

0

另一种解决方案是不依赖于任何特殊功能,并且能够轻松检测错误。请注意,当函数extractUsername()成功时,您将需要释放字符串。

请注意,在C语言中,您只需使用指针算术在字符序列中导航。有一些标准库函数,但它们比从字符串中提取信息的任何东西都要简单得多。

仍然存在其他错误检测问题,例如存在多个“@”等。但这应该足以作为起点。

// Extract "mail:username@example.com"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

const char * MailPrefix = "mail:";
const char AtSign = '@';

char * extractUserName(const char * eMail)
{
    int length = strlen( eMail );
    char * posAtSign = strrchr( eMail, AtSign );
    int prefixLength = strlen( MailPrefix );

    char * toret = (char *) malloc( length + 1 );
    if ( toret != NULL
      && posAtSign != NULL
      && strncmp( eMail, MailPrefix, prefixLength ) == 0 )
    {
        memset( toret, 0, length  +1 );
        strncpy( toret, eMail + prefixLength, posAtSign - prefixLength - eMail );
    }
    else {
        free( toret );
        toret = NULL;
    }

    return toret;
}

int main()
{
    const char * test = "mail:baltasarq@gmail.com";

    char * userName = extractUserName( test );

    if ( userName != NULL ) {
        printf( "User name: '%s'\n", userName );
        free( userName );
    } else {
        fprintf( stderr, "Error: invalid e.mail address\n" );
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

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