【问题标题】:concatenating uppercase letters of two separate strings in c在c中连接两个单独字符串的大写字母
【发布时间】:2015-06-17 12:45:05
【问题描述】:

我正在为即将到来的考试做练习题,但似乎无法弄清楚目标是什么:

编写一个 C 程序来读取两个字符串 s1s2。每个字符串的最大大小为 25。形成两个新的字符串,分别称为 upperlower。 Lower 由s1s2 的小写字母连接而成,而upper 包含两个字符串的大写字母的连接。

例如: 输入:

s1: 绿蓝
s2: busCAR

输出:

上:GREENCAR
下:bluebus

我已经尝试了几天,但无法完全弄清楚。

这是我目前所拥有的:

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

int main()
{
    int i, j;
    char s1[25];
    char s2[25];
    char upper[25];
    char lower[25];

    printf ("Please enter first string: ");
    scanf ("%s", &s1);
    printf ("Please enter second string: ");
    scanf ("%s", &s2);

    strcat (s1, s2);

    for (i=0; i<strlen(s1); i++)
    {
        if (s1[i]>=65 && s1[i] <= 90)
        {
            upper[i] = s1[i];
        }
        else
        {
            lower[i] = s1[i];
        }
    }
    printf ("Upper: %s\n", upper);
    printf ("Lower: %s\n", lower);
}

【问题讨论】:

  • scanf ("%s", &amp;s1); --> scanf ("%24s", s1);,同样适用于s2
  • 投反对票有什么特别的原因吗?关于问题和第一个答案?!

标签: c arrays string scanf


【解决方案1】:

在你的代码中,strcat()的用法

 strcat (s1, s2);

s1 可能没有足够的内存来保存连接的字符串。在这种情况下,它会调用undefined behaviour

从手册页,

如果dest不够大,程序行为是不可预测的;缓冲区溢出是攻击安全程序的常用途径。

根据您的要求

The maximum size of each string is 25

您需要一个长度为 25 X 2(对于 chars)+ 1(对于 null)的目标缓冲区。

此外,它还表明,您需要 s1s2 具有 26 元素,每个元素用于容纳 null。

根据您的要求,您的程序需要进行某些设计更改,因为

  1. 您需要 s1s2 具有 26 元素,每个元素用于容纳 null。

  2. upperlower 的大小都必须是输入大小的总和。如果s1s2 都是完全大写(或小写),请考虑这种情况。所以,你需要upper[51]lower[51]

【讨论】:

  • @RhysHinkson 关于缓冲区大小?我会添加一行。
【解决方案2】:

这是我目前所拥有的::)

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

#define N   25

int main(void)
{
    char s1[N];
    char s2[N];
    char *upper, *lower;
    char *p, *q;
    size_t n1, n2;

    printf( "Please enter first string: " );
    fgets( s1, N, stdin );

    printf( "Please enter second string: " );
    fgets( s2, N, stdin );

    n1 = 0; n2 = 0;

    for ( p = s1; *p; ++p )
    {
        if ( isupper( ( unsigned char )*p ) ) ++n1;
        else if ( islower( ( unsigned char )*p ) ) ++n2;
    }

    for ( p = s2; *p; ++p )
    {
        if ( isupper( ( unsigned char )*p ) ) ++n1;
        else if ( islower( ( unsigned char )*p ) ) ++n2;
    }

    upper = NULL; lower = NULL;

    if ( n1 && ( upper = ( char * )malloc( n1 + 1 ) ) )
    {
        q = upper;

        for ( p = s1; *p; ++p )
        {
            if ( isupper( ( unsigned char )*p ) ) *q++ = *p;
        }

        for ( p = s2; *p; ++p )
        {
            if ( isupper( ( unsigned char )*p ) ) *q++ = *p;
        }

        *q = '\0';
    }       

    if ( n2 && ( lower = ( char * )malloc( n1 + 1 ) ) )
    {
        q = lower;

        for ( p = s1; *p; ++p )
        {
            if ( islower( ( unsigned char )*p ) ) *q++ = *p;
        }

        for ( p = s2; *p; ++p )
        {
            if ( islower( ( unsigned char )*p ) ) *q++ = *p;
        }

        *q = '\0';
    }       

    if ( upper ) printf( "Upper: %s\n", upper );
    if ( lower ) printf( "Lower: %s\n", lower );

    free( upper );
    free( lower );

    return 0;
}

程序输出可能看起来像

Please enter first string: GREENblue
Please enter second string: busCAR
Upper: GREENCAR
Lower: bluebus

如果您的编译器支持可变长度数组,那么您可以使用 VLA(s) 而不是动态分配的数组。

这是一个使用 VLA 的程序

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

#define N   25

int main(void)
{
    char s1[N];
    char s2[N];

    printf( "Please enter first string: " );
    fgets( s1, N, stdin );

    printf( "Please enter second string: " );
    fgets( s2, N, stdin );

    size_t n1 = 0, n2 = 0;

    for ( const char *p = s1; *p; ++p )
    {
        if ( isupper( ( unsigned char )*p ) ) ++n1;
        else if ( islower( ( unsigned char )*p ) ) ++n2;
    }

    for ( const char *p = s2; *p; ++p )
    {
        if ( isupper( ( unsigned char )*p ) ) ++n1;
        else if ( islower( ( unsigned char )*p ) ) ++n2;
    }


    char upper[n1 + 1];
    char lower[n2 + 1];

    if ( n1 )
    {
        char *q = upper;

        for ( const char *p = s1; *p; ++p )
        {
            if ( isupper( ( unsigned char )*p ) ) *q++ = *p;
        }

        for ( const char *p = s2; *p; ++p )
        {
            if ( isupper( ( unsigned char )*p ) ) *q++ = *p;
        }

        *q = '\0';
    }       

    if ( n2 )
    {
        char *q = lower;

        for ( const char *p = s1; *p; ++p )
        {
            if ( islower( ( unsigned char )*p ) ) *q++ = *p;
        }

        for ( const char *p = s2; *p; ++p )
        {
            if ( islower( ( unsigned char )*p ) ) *q++ = *p;
        }

        *q = '\0';
    }       

    if ( n1 ) printf( "Upper: %s\n", upper );
    if ( n2 ) printf( "Lower: %s\n", lower );


    return 0;
}

【讨论】:

    【解决方案3】:

    你还有一个问题:如果你输入一个超过 23 个字符的字符串会发生什么?你的程序会崩溃。

    如果最大大小为 25 个字符,则数组的尺寸必须为 26,而不是 25,以说明终止 NULL。

    为了安全起见,你必须设置 s1[N-1]='\0' 和 s2[N-1] = '\n'。 如果您为第一个字符串输入的内容太长,则第二个字符串最终将成为剩余字符,忽略您在提示第二个字符串后输入的任何内容。

    【讨论】:

    • 详细信息:scanf("%s", &amp;s1);(应该是scanf("%s", s1); 或更好的scanf("%24s", s1);)不会在s1 中保存'\n'。如果用户输入超过 24 个非空白字符,而不是 23 个,代码就会出现问题。
    【解决方案4】:

    我认为您大致了解了一些想法,但遗漏了一些重要的细节:

    • 不要使用strcat 连接两个字符串。您已经将它们中的每一个都放在了自己的缓冲区中,如果您要这样做,最好确保您有足够的内存来存储更大的结果字符串。

    • 1234563其他一切)只是为了安全)。
    • 如果您要打印上/下数组的内容,请确保附加 '\0' 字符,以便您的打印函数将它们解释为字符串。

      j=0; //iterator for lower
      k=0; //iterator for upper
      for (i=0; i<strlen(s1); i++)
      {
          if (s1[i]>='A' && s1[i] <= 'Z') // a bit more clear
          {
              upper[k++] = s1[i];
          }
          else
          {
              if (s1[i]>='a' && s1[i] <= 'z')
              {
                   lower[j++] = s1[i];
              }
              else
              {
                  //print something or just exit...
              }
           }
      }
      
      for (i=0; i<strlen(s2); i++)
      {
          if (s2[i]>='A' && s2[i] <= 'Z') // a bit more clear
          {
              upper[k++] = s2[i];
          }
          else
          {
              if (s2[i]>='a' && s2[i] <= 'z')
              {
                  lower[j++] = s2[i];
              }
              else
              {
                   //print something or just exit...
              }
          }
      }
      //add the string terminator character
      lower[j] = '\0';
      upper[k] = '\0';
      
      //print the result
      printf ("Upper: %s\n", upper);
      printf ("Lower: %s\n", lower);
      

    这应该可以完成工作。

    【讨论】:

    • yes :) 这行得通,我明白了,非常感谢大家的帮助,我会牢记您的所有指点...再次感谢
    • @Pandrei Minor:for (i=0; i&lt;strlen(s1); ... 不必要地重新计算字符串的长度,这需要n 时间 - 这是完成n 次所以O(n*n)。最好len = strlen(s1); for (i=0; i&lt;len; ... 显然不是一个大小为 25 的字符串的大问题。
    • @chux 实际上这不是真的,作为优化过程的一部分,即使在进入 for 循环之前,编译器也会预先计算 strlen(s1)/strlen(s2) 的值。所以实际上和在代码中显式做是一样的。
    • @Pandrei 只有当“知道”是我们都知道和喜爱的strlen() 而不是其他有副作用的strlen() 时,编译器才可以进行优化。此外,正如您评论的那样,只有在编译器分析循环中所有可能的执行路径以确保 s1 没有更改时,才能使用该单个调用。这是许多编译器根本无法满足的要求。见stackoverflow.com/questions/2049480/…
    猜你喜欢
    • 2011-09-15
    • 1970-01-01
    • 1970-01-01
    • 2015-06-18
    • 2011-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多