问题描述
我试图通过将指针传递到我创建的toupper方法中来将字符串转换为大写。逻辑似乎很好,但是我得到了像ëëïà这样的奇怪输出。有什么想法我在这里出错了吗?
#include <iostream>
#include <string.h>
using namespace std;
void toupper(char *);
int main()
{
char name[80];
char *namePtr = name;
cout << "Enter a name :";
cin >> name;
toupper(namePtr);
cout << "The string in Upper Case is: " << name << endl;
}
void toupper(char *p)
{
int asciiValue;
// Loop through each char in the string
for(int i = 0 ; i < strlen(p); i++)
{
asciiValue = (int) p[i];
if(asciiValue >= 97 && asciiValue <= 122)
{
asciiValue = asciiValue + 32;
p[i] = asciiValue;
}
}
}
解决方法
您的问题归结为不良的魔术数字,这使得即使近距离观察也几乎不可能分辨出来,因为它们是魔术数字!
相反,我将使用字符文字使事情变得显而易见:
if(asciiValue >= 'a' && asciiValue <= 'z')
{
asciiValue = asciiValue + ('a' - 'A');
p[i] = asciiValue;
}
现在很明显您要添加错误的值!相反,它应该是:
asciiValue = asciiValue + ('A' - 'a');
,
您的代码不可移植
这是不可移植的代码。确保只使用ASCII编码。 不过,这里是相应的非便携式解决方案:
asciiValue = asciiValue - 32; // - just move in the other direction
如何做得更好?
您当前的代码存在一些问题:
- 大写字母和小写字母之间的差异并不总是32。例如,在EBCDIC中,大写和小写字母之间的差异为
+64
而不是-32
。 - 小写字母的边界也可能不同。
- 对于使用非ASCII语言环境的外语,您可能具有与普通字母(例如,ISO-8859-1)不同范围的特殊字符,其中的小写字母也在224和25'范围内,但带有一个例外。
- 在某些编码中,对于不同的小写字母,您甚至具有不同的规则。采用ISO 8859-3。
'Ŭ'
和'ŭ'
之间的差是-32,而'Ż'
和'ż'
之间的差是-16。 - 最后,不能保证char是未签名的。如果您将ISO-8859-1编码与将字符作为有符号字符管理的编译器结合使用,则整个比较逻辑可能会完全失败。
因此,更安全的方法是使用:isupper()
和toupper()
,它考虑了语言环境。
作为副作用,这甚至可以使用templated version of these functions或wide version促进向完全符合unicode的代码的迁移。
那你为什么不使用真实的字符串呢?
如果有人输入80个或更多字符的名称,您的代码就有缓冲区溢出的危险。您需要确保cin所接受的字符不会超过允许的数目。但是,我建议不要使用更安全的std::string
来代替告诉您如何执行此操作:
void toUpper(string &s)
{
for(auto &p:s) // Loop through each char in the string
if (islower(p))
p =toupper(p);
}
int main()
{
string name;
cout << "Enter a name :";
cin >> name;
toUpper(name);
cout << "The string in Upper Case is: " << name << endl;
}
,
asciiValue = asciiValue-32;
减号代替加号 例: ASCII值“ a”为97
97-32是65,这是大写字母A的ASCII值
,似乎在此if语句中
if(asciiValue >= 97 && asciiValue <= 122)
{
asciiValue = asciiValue + 32;
p[i] = asciiValue;
}
您正在检查当前符号是否为小写ASCII符号。
但是小写的ASCII符号比大写的ASCII符号具有更高的代码。
所以不要添加魔术数字32
asciiValue = asciiValue + 32
您必须减去它
asciiValue = asciiValue - 32
例如,小写的ASCII符号'a'
的编码为97
,大写的symbol 'A'
的编码为65
。
但是在任何情况下,您使用幻数的方法都是不好的,因为例如,它不适用于EBCDIC符号表示形式。
在这种情况下,调用函数strlen
也是无效的。
函数返回指向转换后的字符串的指针会更好。
可以通过以下方式声明和实现该功能
#include <cctype>
//...
char * toUpper( char *s )
{
for ( char *p = s; *p; ++p )
{
if ( std::islower( static_cast<unsigned char>( *p ) ) )
{
*p = std::toupper( static_cast<unsigned char>( *p ) );
}
}
return s;
}
,
更可移植的C ++解决方案是使用std::transform
将字符串转换为小写:
std::string shouting = "AM I SHOUTING";
std::transform(shouting.begin(),shouting.end(),shouting.begin(),tolower);
std::cout << shouting << "\n";
此解决方案不依赖ASCII编码,并且可以与std::tolower
有效的代码集一起使用。