在C语言中将两个字符串合并,并且切换字符。

3
我正在尝试在C语言中合并两个长度不确定的字符串。结果应该是从str1中取第一个字符,然后从str2中取第一个字符,接着从str1中取第二个字符,再从str2中取第二个字符,以此类推。当其中一个字符串已经遍历完时,应将另一个字符串剩余部分附加在后面。
例如:
str1 = "abcdefg";
str2 = "1234";

outputString = "a1b2c3d4efg";

我对C语言还比较新,我的第一个想法是将两个字符串转换为数组,然后尝试遍历这些数组,但我认为可能有一种更简单的方法。欢迎提供示例代码。

更新: 我已经尝试实现下面的答案。我的函数看起来像下面这样。

void strMerge(const char *s1, const char *s2, char *output, unsigned int ccDest)
{
    printf("string1 is %s\n", s1);
    printf("string2 is %s\n", s2);

    while (*s1 != '\0' && *s2 != '\0')
    {
        *output++ = *s1++;
        *output++ = *s2++;
    }
    while (*s1 != '\0')
        *output++ = *s1++;
    while (*s2 != '\0')
        *output++ = *s2++;
    *output = '\0';

    printf("merged string is %s\n", *output);
}

但是编译时我收到了一个警告:

$ gcc -g -std=c99 strmerge.c -o strmerge
strmerge2.c: In function ‘strMerge’:
strmerge2.c:41:5: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat]

当我运行它时,它不起作用:

./strmerge abcdefg 12314135
string1 is abcdefg
string2 is 12314135
merged string is (null)

为什么它认为第二个参数是int,而我如何将其修复为char类型?如果我在printf中删除output前面的“*”,它就不会报编译错误,但该函数仍然无法正常工作。


那么你的实际问题是什么? - Dolda2000
用户输入两个字符串。我需要返回一个包含这两个字符串合并的单个字符串。 - tbenz9
你的一个问题是空间管理——输出将放在哪里。完成这一步后,代码就相当简单了。虽然两个输入字符串都没有完成,但从每个字符串中复制一个字符到输出中。一旦一个字符串完成,你就需要运行另外两个循环,一个用来耗尽第一个字符串,另一个用来耗尽第二个字符串。只有其中之一将被执行(取决于哪个字符串较长),但不要试图找出哪一个;没有必要(假设你正确编写了循环)。 - Jonathan Leffler
输出被放入一个新的字符串中,然后使用printf简单地打印出来。目标缓冲区为128个字符,但目标字符串不应超过该长度。 - tbenz9
2
编译错误是因为你应该打印output而不是*output(后者是字符串的第一个字符而不是整个字符串本身)。此外,output指向字符串末尾的空字节。如果你想打印整个字符串,你必须在进入函数时保留output的原始值。 - Jonathan Leffler
4个回答

3
char* getMerged(const char* str1, const char* str2) {
  char* str = malloc(strlen(str1)+strlen(str2)+1);
  int k=0,i;
  for(i=0;str1[i] !='\0' && str2[i] !='\0';i++) {
         str[k++] = str1[i];
         str[k++] = str2[i];
  }

  str[k]='\0';
  if (str1[i] != '\0') {
        strcpy(&str[k], &str1[i]); 
  } else if (str2[i] != '\0') {
        strcpy(&str[k], &str2[i]);
  }
 return str; 
}

非常不错。你可以声明参数为const char *,因为你不会修改输入的字符串。此外,由于您知道最后一个字符的位置,因此可以使用strcpy()而无需首先以空字符结尾的形式使用strcat()函数。此外,您不想将整个str1str2复制到字符串中;您没有为此分配足够的空间。使用strcpy()避免了重新扫描输出的操作,而这是在您以您的方式调用strcat()时必须执行的操作:if (str1[i] != '\0') strcpy(&str[k], &str1[i]); else if (str2[i] != '\0') strcpy(&str[k], &str2[i]);. - Jonathan Leffler

3
以下代码确保字符串不会溢出,通过将输出字符串长度设置为两个输入字符串的长度,并使用 fgets() 确保输入字符串没有溢出。另一种备选设计是动态内存分配(malloc()等),但代价是调用代码必须 free() 分配的空间。另一种设计是将输出缓冲区的长度传递给函数,以便它可以确保不会溢出。

测试程序不发出提示:很容易添加一个函数来实现此功能。

代码

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

void interleave_strings(const char *s1, const char *s2, char *output)
{
    while (*s1 != '\0' && *s2 != '\0')
    {
        *output++ = *s1++;
        *output++ = *s2++;
    }
    while (*s1 != '\0')
        *output++ = *s1++;
    while (*s2 != '\0')
        *output++ = *s2++;
    *output = '\0';
}

int main(void)
{
    char line1[100];
    char line2[100];
    char output[200];
    if (fgets(line1, sizeof(line1), stdin) != 0 &&
        fgets(line2, sizeof(line2), stdin) != 0)
    {
        char *end1 = line1 + strlen(line1) - 1;
        char *end2 = line2 + strlen(line2) - 1;
        if (*end1 == '\n')
            *end1 = '\0';
        if (*end2 == '\n')
            *end2 = '\0';
        interleave_strings(line1, line2, output);
        printf("In1: <<%s>>\n", line1);
        printf("In2: <<%s>>\n", line2);
        printf("Out: <<%s>>\n", output);
    }
}

例子输出

$ ./interleave
abcdefgh
1234
In1: <<abcdefgh>>
In2: <<1234>>
Out: <<a1b2c3d4efgh>>
$

谢谢Jonathan,你的例子非常有帮助,继续保持好工作。 - tbenz9
这两个字符串包含所有的存储空间,再加上一个字节来容纳它们的两个终止符,以便容纳合并后的字符串。有人知道如何在原地执行此合并吗? - user1899861

2
您的strMerge打印null,因为您打印了* output的valueAt(),而该值在上一步被赋为null。
#include<stdio.h>
#include<string.h>
//strMerge merges two string as per user requirement
void strMerge(const char *s1, const char *s2, char *output)
{
    printf("string1 is %s\n", s1);
    printf("string2 is %s\n", s2);
    while (*s1 != '\0' && *s2 != '\0')
    {
        *output++= *s1++;
    *output++ = *s2++;
    }
    while (*s1 != '\0')
        *output++=*s1++;
    while (*s2 != '\0')
        *output++ = *s2++;
    *output='\0';
}

int main()
{
    char *str1="abcdefg"; 
    char *str2="1234";
    char *output=malloc(strlen(str1)+strlen(str2)+1); //allocate memory 7+4+1 = 12 in this case
    strMerge(str1,str2,output); 
    printf("%s",output);
    return 0;
}
OUTPUT:
   string1 is abcdefg
   string2 is 1234
   a1b2c3d4efg

谢谢,多亏了你简单明了的代码,我只需要稍微调整一下就让我的代码正常运行了。 - tbenz9
从风格上讲,输出应该是最左边的参数,并且函数应该返回它,就像strcat()一样。这在C标准库中是普遍使用的,因此函数返回可以代替调用函数时的参数。例如,printf("%s", strMerge(output, str1, str2)); 此外,养成使用calloc()而不是malloc()的习惯。与new一样,calloc()初始化内存,使添加空终止符变得不必要,并且使调试变得更加容易。 - user1899861
如下所示,使用&而不是&&在while()测试中可以提高20-45%的性能。 - user1899861

1
在C语言中,两个字符串都已经是可以通过它们的指针访问的数组。你只需要创建一个足够大的新缓冲区,然后将其复制到其中。
例如,像这样:
int str1Length = strlen(str1);
int str2Length = strlen(str2);

char* output = (char*) malloc(str1Length + str2Length + 1);

int j = 0;
for (int i = 0; i < str1Length; i++)
{
    output[j++] = str1[i];
    if (str2Length < i)
        output[j++] = str2[i];
}

if (str2Length > str1Length)
{
    for (int i = str2Length - str1Length; i < str2Length; i++)
    {
        output[j++] = str2[i];
    }
}

1
你确定你在写C语言吗?我知道C++有一个new,但是它出现在C语言中对我来说是新闻。MSVC编译器会将大多数C代码编译成C++代码,这可能会让事情变得混乱...但请重新用纯C语言编写或删除。 - Jonathan Leffler
是的,我倾向于用“C++色眼镜”来看问题。将new转换为malloc并不是什么大问题,而且肯定不值得花费downvotes :) - Tawnos
所有这些下标使代码杂乱无章。正确的替代new的方法是calloc()而不是malloc(),因为calloc()会初始化其内存。 - user1899861

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