如何在C语言中从给定的字符串中删除\n或\t?

8

如何在C语言中去除字符串中的所有 \n 和 \t?

5个回答

16
这在我的简单测试中有效。它是否原地执行:
#include <stdio.h>

void strip(char *s) {
    char *p2 = s;
    while(*s != '\0') {
        if(*s != '\t' && *s != '\n') {
            *p2++ = *s++;
        } else {
            ++s;
        }
    }
    *p2 = '\0';
}

int main() {
    char buf[] = "this\t is\n a\t test\n test";
    strip(buf);
    printf("%s\n", buf);
}

为了取悦Chris,这里有一个版本,它将结果放在一个新的malloc缓冲区中并返回它(因此它适用于文本字面量)。您需要free结果。

char *strip_copy(const char *s) {
    char *p = malloc(strlen(s) + 1);
    if(p) {
        char *p2 = p;
        while(*s != '\0') {
            if(*s != '\t' && *s != '\n') {
                *p2++ = *s++;
            } else {
                ++s;
            }
        }
        *p2 = '\0';
    }
    return p;
}

在原地执行此操作相当不安全。如果 main() 的第一行更改为 char *buf = ...; 会怎样?如果在更复杂的代码中使用,并且编码人员忘记了哪些参数是可写缓冲区,哪些不是呢? - Chris Lutz
如果您需要复制,那么可以先复制。这样的代码仍然相当简单。 - Evan Teran
@Lutz,我不明白传递char*char[]有什么区别。另外,在strip函数中存在一个错误:字符串没有以空字符结尾。 - strager
1
@strager: 他关心的是传递类似于 char *buf = "hello\tworld" 的东西,这是非法的,因为您无法修改指向文字常量的指针。我的 strip_copy 解决了这个问题。 - Evan Teran
1
但是要加一分,因为您打破了我的期望,strip-in-place 通常会很低效。 - Chris Lutz
显示剩余5条评论

5

如果你想用其他东西替换\n或\t,可以使用函数strstr()。它返回一个指向函数中包含某个字符串的第一个位置的指针。例如:

// Find the first "\n".
char new_char = 't';
char* pFirstN = strstr(szMyString, "\n");
*pFirstN = new_char;

您可以使用循环来查找所有的 \n 和 \t。如果您想要“去除”它们,也就是从字符串中删除它们,您需要实际上使用与上面相同的方法,但每次在找到 \n 或 \t 时都要将字符串的内容“向后”复制,这样“this i\ns a test”就变成了:“this is a test”。您可以使用 memmove(而不是 memcpy,因为 src 和 dst 指向重叠的内存)来实现,如下所示:
char* temp = strstr(str, "\t");
// Remove \n.
while ((temp = strstr(str, "\n")) != NULL) {
// Len is the length of the string, from the ampersand \n, including the \n.
     int len = strlen(str);
 memmove(temp, temp + 1, len); 
}

你需要再次重复这个循环来去除 \t。
注意:这两种方法都是原地操作的。这可能不安全!(详见 Evan Teran 的评论)。此外,这些方法并不是非常高效,尽管它们利用了库函数来执行部分代码,而不是自己编写代码。

这看起来对于长字符串来说非常低效。你一遍又一遍地搜索字符串(从开头开始)。此外,每次找到一个字符时都要执行 strlen。最后,每次找到一个字符时都要复制字符串的尾部... - Evan Teran
你说得对,效率确实不是一个大问题(这实际上是从我留下来的代码中直接提取出来的,它只能处理小字符串)。我认为strlen可以移动到字符串外部,搜索可以每次从上次找到的位置开始,而不是从开头开始,从而消除“每次搜索问题”。但是,如果他想要就地完成操作,我看不到任何消除memmove需求的方法。有什么建议吗? - Edan Maor
如果你想看一个高效的原地删除字符串的方法,那就看看我的回答吧 :-P。 - Evan Teran
是的,我在发表评论后立即查看了它,现在感觉很愚蠢 :) 至少现在我有一些可以在我的项目中更改的东西... - Edan Maor
这是在看到Evan的高效版本之前,我认为答案应该是什么。+1 个赞给库函数 - memmove() 可惜相对较少人知道。 - Chris Lutz
我尝试使用这种方法,但在clang的-fsanitize=address下彻底失败了。 - Vinícius Ferrão

4

基本上,你有两种方式来做这件事:你可以创建一个原始字符串的副本,去掉所有的'\t''\n'字符,或者你可以直接在原字符串上进行修改。然而,我敢打赌第一种选择会更快,而且我向你保证它也更安全。

因此,我们将创建一个函数:

char *strip(const char *str, const char *d);

我们希望使用 strlen()malloc() 来分配一个与我们的 str 缓冲区大小相同的新的 char * 缓冲区。然后我们逐个字符地遍历 str。如果该字符不包含在 d 中,我们将其复制到新的缓冲区中。我们可以使用类似 strchr() 的函数来判断每个字符是否在字符串 d 中。完成后,我们就有了一个新的缓冲区,其中包含旧缓冲区中除字符串 d 中的字符之外的所有内容,因此我们只需返回它即可。我不会给你示例代码,因为这可能是作业,但以下是样例用法,以展示它如何解决你的问题:
char *string = "some\n text\t to strip";

char *stripped = strip(string, "\t\n");

提供所有学习如何做到这一点所需的信息,这将对初学者非常有用。 - Evan Teran

1
这是一个 C 字符串函数,它将查找 accept 中的任何字符,并返回该位置的指针,如果未找到,则返回 NULL。
#include <string.h>

char *strpbrk(const char *s, const char *accept);

例子:

char search[] = "a string with \t and \n";

char *first_occ = strpbrk( search, "\t\n" );

first_occ将指向search中的制表符或第15个字符。您可以替换它并再次调用循环,直到所有内容都被替换。


1

我喜欢尽可能让标准库去完成更多的工作,因此我会使用类似于 Evan 的方案,但会加入 strspn()strcspn()

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

#define SPACE " \t\r\n"

static void strip(char *s);
static char *strip_copy(char const *s);

int main(int ac, char **av)
{
    char s[] = "this\t is\n a\t test\n test";
    char *s1 = strip_copy(s);
    strip(s);
    printf("%s\n%s\n", s, s1);
    return 0;
}

static void strip(char *s)
{
    char *p = s;
    int n;
    while (*s)
    {
        n = strcspn(s, SPACE);
        strncpy(p, s, n);
        p += n;
        s += n + strspn(s+n, SPACE);
    }
    *p = 0;
}

static char *strip_copy(char const *s)
{
    char *buf = malloc(1 + strlen(s));
    if (buf)
    {
        char *p = buf;
        char const *q;
        int n;
        for (q = s; *q; q += n + strspn(q+n, SPACE))
        {
            n = strcspn(q, SPACE);
            strncpy(p, q, n);
            p += n;
        }
        *p++ = '\0';
        buf = realloc(buf, p - buf);
    }
    return buf;
}

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