【问题标题】:Converting words from camelCase to snake_case in C在C中将单词从camelCase转换为snake_case
【发布时间】:2017-11-28 11:14:11
【问题描述】:

我要编码的是,如果我输入camelcase,它应该只打印出camelcase,但如果包含任何大写字母,例如,如果我输入camelCase,它应该打印出@987654325 @。

下面是我正在处理的问题,但问题是,如果我输入 camelCase,它会打印出 camel_ase

谁能告诉我原因以及如何解决?

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

int main() {
    char ch;
    char input[100];
    int i = 0;

    while ((ch = getchar()) != EOF) {
        input[i] = ch;
        if (isupper(input[i])) {
            input[i] = '_';
            //input[i+1] = tolower(ch);
        } else {
            input[i] = ch;
        }
        printf("%c", input[i]);

        i++;
    }
}

【问题讨论】:

  • 请注意,getchar 返回一个 int。这实际上对您检查 EOF 很重要。
  • 意思是 camelCase(不是carmel)你把它和snake_case混淆了。

标签: c computer-science


【解决方案1】:

首先查看您的代码并考虑当有人输入超过 100 个字符的单词时会发生什么 -> 未定义的行为。如果您使用缓冲区进行输入,您总是必须添加检查,以免溢出此缓冲区。

但是,当您直接打印字符时,为什么还需要缓冲区呢?您展示的方法完全没有必要。试试这个:

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

int main()
{
    int ch;
    int firstChar = 1; // needed to also accept PascalCase
    while((ch = getchar())!= EOF)
    {
        if(isupper(ch))
        {
            if (!firstChar) putchar('_');
            putchar(tolower(ch));

        } else
        {
            putchar(ch);
        }
        firstChar = 0;
    }
}

旁注:我将ch 的类型更改为int。这是因为getchar() 返回一个intputchar()isupper()islower() 采用int,并且它们都使用unsigned charEOF 的值。由于允许对char 进行签名,因此在带有签名char 的平台上,您将获得未定义的行为,以否定char 调用这些函数。我知道,这有点复杂。解决此问题的另一种方法是,在调用将unsigned char 的值作为int 的函数时,始终将char 转换为unsigned char


当您使用缓冲区时,它现在没用,您可能会对那里感兴趣是否充分利用缓冲区的可能解决方案:一次读取和写入一整行。这比为每个字符调用一个函数稍微高效一些。这是一个这样做的例子:

#include <stdio.h>

static size_t toSnakeCase(char *out, size_t outSize, const char *in)
{
    const char *inp = in;
    size_t n = 0;
    while (n < outSize - 1 && *inp)
    {
        if (*inp >= 'A' && *inp <= 'Z')
        {
            if (n > outSize - 3)
            {
                out[n++] = 0;
                return n;
            }
            out[n++] = '_';
            out[n++] = *inp + ('a' - 'A');
        }
        else
        {
            out[n++] = *inp;
        }
        ++inp;
    }
    out[n++] = 0;
    return n;
}

int main(void)
{
    char inbuf[512];
    char outbuf[1024]; // twice the lenght of the input is upper bound

    while (fgets(inbuf, 512, stdin))
    {
        toSnakeCase(outbuf, 1024, inbuf);
        fputs(outbuf, stdout);
    }
    return 0;
}

这个版本也避免了isupper()tolower(),但是牺牲了便携性。它在字符编码具有字母顺序并且在小写字母之前具有大写字母的情况下才有效。对于 ASCII,这些假设成立。请注意,被视为(大写)字母的内容也可能取决于语言环境。上面的程序只适用于英文字母 A-Z。

【讨论】:

  • 您可以使用*inp ^ (1u &lt;&lt; 5) 代替*inp + ('a' - A')`。每个都做同样的事情,你只是切换案例位。
【解决方案2】:

要以您显示的形式输出输入的字符,无需使用数组。该程序可以如下所示

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

int main( void )
{
    int c;

    while ((c = getchar()) != EOF && c != '\n')
    {
        if (isupper(c))
        {
            putchar('_');
            c = tolower(c);
        }
        putchar(c);
    }

    putchar('\n');

    return 0;
}

如果你想使用一个字符数组,你应该为终止零保留一个元素,如果你希望该数组包含一个字符串。

在这种情况下,程序可能看起来像

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

int main( void )
{
    char input[100];
    const size_t N = sizeof(input) / sizeof(*input);

    int c;
    size_t i = 0;

    while ( i + 1 < N && (c = getchar()) != EOF && c != '\n')
    {
        if (isupper(c))
        {
            input[i++] = '_';
            c = tolower(c);
        }
        if ( i + 1 != N ) input[i++] = c;
    }

    input[i] = '\0';

    puts(input);

    return 0;
}

【讨论】:

  • 通过一个测试关闭:i + 1 &lt; N -> i + 2 &lt; N
  • @chqrlie 为什么你决定应该使用 I + 2
  • 因为如前所述,你会对一长串大写字母有未定义的行为 ;-)
  • @chqrlie 我不明白为什么会有未定义的行为。
  • 如果i == N-2时有大写字母,则将i递增两次,并将空字节存储到input[N]中。
【解决方案3】:

您的代码中存在多个问题:

  • ch 定义为 char:如果 c 未定义为 int,则无法正确测试文件结尾。 getc() 可以返回所有unsigned char 类型的值加上特殊值EOF,它是负数。将ch 定义为int

  • 您将字节存储到数组input 并使用isupper(input[i])isupper() 仅针对getc() 返回的值定义,而不针对char 类型的潜在负值(如果此类型在目标系统上签名)。使用isupper(ch)isupper((unsigned char)input[i])

  • 在将字节存储到input[i] 之前,您没有检查i 是否足够小,从而导致潜在的缓冲区溢出。请注意,对于您的问题,不必将字符存储到数组中。

  • 您应该将'_' 插入数组转换为小写的字符。这是你的主要问题。

  • 是否要将Main 转换为_mainmain 或保留为Main 是规范问题。

这是一个更简单的版本:

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

int main(void) {
    int c;

    while ((c = getchar()) != EOF) {
        if (isupper(c)) {
            putchar('_');
            putchar(tolower(c));
        } else {
            putchar(c);
        }
    }
    return 0;
}

【讨论】:

    【解决方案4】:

    你的代码有两个问题:

    • 您在if 的每个分支中插入一个字符,而其中一个应该插入两个字符,并且
    • 您可以随时打印字符,但第一个分支应该同时打印_ch

    您可以通过在插入时使用 i++ 递增 i 并在末尾打印整个单词来解决此问题:

    int ch; // <<== Has to be int, not char
    char input[100];
    int i = 0;
    
    while((ch = getchar())!= EOF && (i < sizeof(input)-1)) {
        if(isupper(ch)) {
            if (i != 0) {
                input[i++] = '_';
            }
            ch = tolower(ch);
        }
        input[i++] = ch;
    }
    input[i] = '\0'; // Null-terminate the string
    printf("%s\n", input);
    

    Demo.

    【讨论】:

    • 当我运行你为我写的代码时,它什么也没有打印出来。我怎样才能看到 printf??
    • @seung 我添加了一个演示,它打印了预期的字符串,请看一下。
    • 哦,谢谢!但是,作为最后一个问题,当我为第一个字符输入大写时,它也会使其成为下划线,但我不希望第一个字符被转换为'_'..我应该如何解决这个问题?
    • @seung 在条件中添加对i != 0 的检查。
    • 用“LoremIpsumDolor”字符串测试
    【解决方案5】:

    我不确切知道如何用 C 编写代码,但我认为你应该这样做。

    if(isupper(input[i]))
    {
        input[i] = tolower(ch);
        printf("_");
    
    } else
    {
        input[i] = ch;
    }
    

    【讨论】:

    • 这会在大写关键字的开头添加很多下划线,这可能是您不想要的。
    猜你喜欢
    • 2012-03-20
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2012-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-13
    相关资源
    最近更新 更多