将字符串转换为 char 并检查字母
Turning string into char and checking for letters
我的一段代码有一些问题,我认为当我尝试使用输入的输入对其进行测试时会导致错误,导致 Visual Studios 冻结并显示一条错误消息,显示 0x74A1DDC2 处未处理的异常。这是我写的代码:
bool isValidRomanNumber(string test) {
//Validates that a roman number was entered
char char_array[10];
strcpy(char_array, test.c_str());
for (int i = 0; i < 10; i++) {
if ('I' == char_array[i] || 'V' == char_array[i] || 'X' == char_array[i]|| 'L' == char_array[i] || 'C'== char_array[i] || 'D' == char_array[i] || 'M' == char_array[i]) {
cout << test << endl;
return true;
}
else {
return false;
}
}
}
我知道是这个函数导致了错误,因为我测试过的程序的所有其他部分都运行良好。我是 C++ 的新手,所以我不知道我做错了什么,所以非常欢迎提出建议。
您不必将 std::string
的内容复制到 char
数组。
#include <cctype>
#include <string>
bool isRomanDigit(char ch)
{
ch = std::toupper(ch);
return ch == 'I' || ch == 'V' || ch == 'X' || ch == 'L' ||
ch == 'C' || ch == 'D' || ch == 'M';
}
bool isValidRomanNumber(std::string const &test)
{
for (auto ch : test)
if (!isRomanDigit(ch))
return false;
return true;
}
简单检查是否找不到无效数字:
bool romanDigitsOnly(const std::string& number) {
return number.find_first_not_of("IVXLCDM") == std::string::npos;
}
find_first_not_of returns npos if none 在字符串中找到指定的字符。
请注意,这仅检查有效数字,而不检查 valid roman numbers.
这是一个不使用库函数来检查罗马数字的简单替代方法。只是一个循环和一个 switch
语句。
bool isValidRomanDigits(const std::string &str)
{
for (char ch : str) {
switch (ch) {
case 'I':
case 'V':
case 'X':
case 'L':
case 'C':
case 'D':
case 'M':
// valid character, do nothing
break;
default:
// invalid character
return false;
}
}
return true;
}
令我印象深刻的是 Clang 可以将其优化为少量指令 (live @ godbolt):
mov rcx, qword ptr [rdi + 8]
test rcx, rcx
je .LBB0_5
mov rdx, qword ptr [rdi]
mov esi, 2623043
.LBB0_2: # =>This Inner Loop Header: Depth=1
movsx edi, byte ptr [rdx]
xor eax, eax
add edi, -67
cmp edi, 21
ja .LBB0_6
bt esi, edi
jae .LBB0_6
add rdx, 1
add rcx, -1
jne .LBB0_2
.LBB0_5:
mov al, 1
.LBB0_6:
ret
这里有很多优秀的解决方案。但是,我想解释一下为什么你的代码不起作用,因为如果你不学会避免这些错误,这些错误会让你的生活变得艰难。
1)循环逻辑错误
首先,您的循环将仅测试字符串的第一个字符:循环中 if
子句的第一次执行必然导致 return
,而不会检查任何其他字符!
解决方案:检查每个字符,return只有在循环成功检查完所有字符后才为真。相反,如果任何字符无效,return 立即为 false。
2) 你可能处理了太多字符
其次,您的循环恰好检查了 10 个字符。如果您的输入字符串较短,strcpy()
将引入尾随 '[=15=]'
来标记 c 字符串的结尾(并且此字符不匹配任何有效的罗马字符),并将保留剩余的字符未初始化(因此包含垃圾,很可能也不是罗马)。
解决方法: 确保到达 c 字符串末尾时循环条件为假。
3) 如果输入字符串太长会怎样?
第三,strcpy()
不安全。如果您的输入字符串长度为 9 个字符,strcpy()
会将 10 个字符复制到其目标中(因为尾随 '[=15=]'
终止符)。不幸的是,如果您的输入字符串较长,strcpy()
将继续复制超出为目标分配的存储空间的额外字符。这将导致内存损坏:它可能导致无法观察到任何东西,或者可能导致程序冻结或任何其他奇怪的行为。
解决方法:使用strncpy()
避免缓冲区溢出的风险
调整您的原始代码,没有其他改进
bool isValidRomanNumber(string test) {
char char_array[10];
strncpy(char_array, test.c_str(), 10);
for (int i = 0; i < 10 && char_array[i]; i++) {
if ('I' != char_array[i] && 'V' != char_array[i] && 'X' != char_array[i]&& 'L' != char_array[i] && 'C'!= char_array[i] && 'D' != char_array[i] && 'M' != char_array[i]) {
return false;
}
}
return true;
}
但这是遗留的 c++,不是很酷的现代 c++
一个更好的选择是摆脱 c 字符串并只使用更安全的 c++ string
。然后你也不需要担心存储分配。
好消息是它很简单:您可以直接访问输入字符串的字符:
bool isValidRomanNumber(string test) {
for (int i = 0; i < test.size(); i++) {
if ('I' != test[i] && 'V' != test[i] && 'X' != test[i]&& 'L' != test[i] && 'C'!= test[i] && 'D' != test[i] && 'M' != test[i]) {
return false;
}
}
return true;
}
如果您的输入字符串长度为 1000 个字符也没问题:-) 它会检查所有字符!
为了更好的解决方案,您现在可以查看其他答案
我的一段代码有一些问题,我认为当我尝试使用输入的输入对其进行测试时会导致错误,导致 Visual Studios 冻结并显示一条错误消息,显示 0x74A1DDC2 处未处理的异常。这是我写的代码:
bool isValidRomanNumber(string test) {
//Validates that a roman number was entered
char char_array[10];
strcpy(char_array, test.c_str());
for (int i = 0; i < 10; i++) {
if ('I' == char_array[i] || 'V' == char_array[i] || 'X' == char_array[i]|| 'L' == char_array[i] || 'C'== char_array[i] || 'D' == char_array[i] || 'M' == char_array[i]) {
cout << test << endl;
return true;
}
else {
return false;
}
}
}
我知道是这个函数导致了错误,因为我测试过的程序的所有其他部分都运行良好。我是 C++ 的新手,所以我不知道我做错了什么,所以非常欢迎提出建议。
您不必将 std::string
的内容复制到 char
数组。
#include <cctype>
#include <string>
bool isRomanDigit(char ch)
{
ch = std::toupper(ch);
return ch == 'I' || ch == 'V' || ch == 'X' || ch == 'L' ||
ch == 'C' || ch == 'D' || ch == 'M';
}
bool isValidRomanNumber(std::string const &test)
{
for (auto ch : test)
if (!isRomanDigit(ch))
return false;
return true;
}
简单检查是否找不到无效数字:
bool romanDigitsOnly(const std::string& number) {
return number.find_first_not_of("IVXLCDM") == std::string::npos;
}
find_first_not_of returns npos if none 在字符串中找到指定的字符。 请注意,这仅检查有效数字,而不检查 valid roman numbers.
这是一个不使用库函数来检查罗马数字的简单替代方法。只是一个循环和一个 switch
语句。
bool isValidRomanDigits(const std::string &str)
{
for (char ch : str) {
switch (ch) {
case 'I':
case 'V':
case 'X':
case 'L':
case 'C':
case 'D':
case 'M':
// valid character, do nothing
break;
default:
// invalid character
return false;
}
}
return true;
}
令我印象深刻的是 Clang 可以将其优化为少量指令 (live @ godbolt):
mov rcx, qword ptr [rdi + 8]
test rcx, rcx
je .LBB0_5
mov rdx, qword ptr [rdi]
mov esi, 2623043
.LBB0_2: # =>This Inner Loop Header: Depth=1
movsx edi, byte ptr [rdx]
xor eax, eax
add edi, -67
cmp edi, 21
ja .LBB0_6
bt esi, edi
jae .LBB0_6
add rdx, 1
add rcx, -1
jne .LBB0_2
.LBB0_5:
mov al, 1
.LBB0_6:
ret
这里有很多优秀的解决方案。但是,我想解释一下为什么你的代码不起作用,因为如果你不学会避免这些错误,这些错误会让你的生活变得艰难。
1)循环逻辑错误
首先,您的循环将仅测试字符串的第一个字符:循环中 if
子句的第一次执行必然导致 return
,而不会检查任何其他字符!
解决方案:检查每个字符,return只有在循环成功检查完所有字符后才为真。相反,如果任何字符无效,return 立即为 false。
2) 你可能处理了太多字符
其次,您的循环恰好检查了 10 个字符。如果您的输入字符串较短,strcpy()
将引入尾随 '[=15=]'
来标记 c 字符串的结尾(并且此字符不匹配任何有效的罗马字符),并将保留剩余的字符未初始化(因此包含垃圾,很可能也不是罗马)。
解决方法: 确保到达 c 字符串末尾时循环条件为假。
3) 如果输入字符串太长会怎样?
第三,strcpy()
不安全。如果您的输入字符串长度为 9 个字符,strcpy()
会将 10 个字符复制到其目标中(因为尾随 '[=15=]'
终止符)。不幸的是,如果您的输入字符串较长,strcpy()
将继续复制超出为目标分配的存储空间的额外字符。这将导致内存损坏:它可能导致无法观察到任何东西,或者可能导致程序冻结或任何其他奇怪的行为。
解决方法:使用strncpy()
避免缓冲区溢出的风险
调整您的原始代码,没有其他改进
bool isValidRomanNumber(string test) {
char char_array[10];
strncpy(char_array, test.c_str(), 10);
for (int i = 0; i < 10 && char_array[i]; i++) {
if ('I' != char_array[i] && 'V' != char_array[i] && 'X' != char_array[i]&& 'L' != char_array[i] && 'C'!= char_array[i] && 'D' != char_array[i] && 'M' != char_array[i]) {
return false;
}
}
return true;
}
但这是遗留的 c++,不是很酷的现代 c++
一个更好的选择是摆脱 c 字符串并只使用更安全的 c++ string
。然后你也不需要担心存储分配。
好消息是它很简单:您可以直接访问输入字符串的字符:
bool isValidRomanNumber(string test) {
for (int i = 0; i < test.size(); i++) {
if ('I' != test[i] && 'V' != test[i] && 'X' != test[i]&& 'L' != test[i] && 'C'!= test[i] && 'D' != test[i] && 'M' != test[i]) {
return false;
}
}
return true;
}
如果您的输入字符串长度为 1000 个字符也没问题:-) 它会检查所有字符!
为了更好的解决方案,您现在可以查看其他答案