在 C 中将单词从驼峰式转换为 snake_case
Converting words from camelCase to snake_case in C
我要编写的代码是,如果我输入 camelcase
,它应该只打印出 camelcase
,但是如果其中包含任何大写字母,例如,如果我输入 camelCase
], 它应该打印出 camel_case
。
下面是我正在处理的,但问题是,如果我输入 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++;
}
}
你的代码有两个问题:
- 你在
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] = '[=10=]'; // Null-terminate the string
printf("%s\n", input);
我不太清楚如何用 C 编写代码,但我认为您应该这样做。
if(isupper(input[i]))
{
input[i] = tolower(ch);
printf("_");
} else
{
input[i] = ch;
}
首先查看您的代码并思考当有人输入超过 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()
returns 一个 int
、putchar()
、isupper()
和 islower()
取一个 int
并且它们都使用一个值unsigned char
或 EOF
。由于允许对 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。
您的代码中存在多个问题:
ch
定义为 char
:如果 c
未定义为 int
,则无法正确测试文件结尾。 getc()
可以 return 类型 unsigned char
的所有值加上特殊值 EOF
,它是负数。将 ch
定义为 int
.
您将字节存储到数组 input
中并使用 isupper(input[i])
。 isupper()
仅针对由 getc()
编辑的值 return 定义,而不针对 char
类型的潜在负值定义(如果此类型已在目标系统上签名)。使用 isupper(ch)
或 isupper((unsigned char)input[i])
.
在将字节存储到 input[i]
之前,您没有检查 i
是否足够小,导致潜在的缓冲区溢出。请注意,对于您的问题,不必将字符存储到数组中。
你应该在数组中插入'_'
并且字符转换为小写。这是你的主要问题。
是否要将 Main
转换为 _main
、main
或保留为 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;
}
要按照您显示的形式输出输入的字符,不需要使用数组。该程序可以如下所示
#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] = '[=11=]';
puts(input);
return 0;
}
我要编写的代码是,如果我输入 camelcase
,它应该只打印出 camelcase
,但是如果其中包含任何大写字母,例如,如果我输入 camelCase
], 它应该打印出 camel_case
。
下面是我正在处理的,但问题是,如果我输入 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++;
}
}
你的代码有两个问题:
- 你在
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] = '[=10=]'; // Null-terminate the string
printf("%s\n", input);
我不太清楚如何用 C 编写代码,但我认为您应该这样做。
if(isupper(input[i]))
{
input[i] = tolower(ch);
printf("_");
} else
{
input[i] = ch;
}
首先查看您的代码并思考当有人输入超过 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()
returns 一个 int
、putchar()
、isupper()
和 islower()
取一个 int
并且它们都使用一个值unsigned char
或 EOF
。由于允许对 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。
您的代码中存在多个问题:
ch
定义为char
:如果c
未定义为int
,则无法正确测试文件结尾。getc()
可以 return 类型unsigned char
的所有值加上特殊值EOF
,它是负数。将ch
定义为int
.您将字节存储到数组
input
中并使用isupper(input[i])
。isupper()
仅针对由getc()
编辑的值 return 定义,而不针对char
类型的潜在负值定义(如果此类型已在目标系统上签名)。使用isupper(ch)
或isupper((unsigned char)input[i])
.在将字节存储到
input[i]
之前,您没有检查i
是否足够小,导致潜在的缓冲区溢出。请注意,对于您的问题,不必将字符存储到数组中。你应该在数组中插入
'_'
并且字符转换为小写。这是你的主要问题。是否要将
Main
转换为_main
、main
或保留为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;
}
要按照您显示的形式输出输入的字符,不需要使用数组。该程序可以如下所示
#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] = '[=11=]';
puts(input);
return 0;
}