将字符串转换为char并检查字母

时间:2018-09-01 20:59:51

标签: c++ unhandled-exception

我的一段代码存在一些问题,我认为当我尝试使用输入的输入对其进行测试时,这会导致错误,从而导致Visual Studio冻结并显示一条错误消息,指出未处理的异常位于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 ++的新手,所以我不知道自己在做什么错,因此非常欢迎提出建议。

4 个答案:

答案 0 :(得分:4)

只需检查以确保找不到无效数字:

bool romanDigitsOnly(const std::string& number) {
  return number.find_first_not_of("IVXLCDM") == std::string::npos;
}
如果在字符串中找不到指定的字符,则

find_first_not_of返回npos。 请注意,这只会检查有效数字,而不会检查valid roman numbers

答案 1 :(得分:2)

您不必将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;
}

答案 2 :(得分:2)

这里有很多出色的解决方案。但是,我想解释一下为什么您的代码不起作用,因为如果您不学会避免这些错误,这些错误会使您的生活更加艰难。

1)循环中的逻辑错误

首先,您的循环将仅测试字符串的第一个字符:循环中if子句的首次执行将必然导致return,而不会检查任何其他字符!

解决方案:检查每个字符并仅在循环成功检查所有字符之后才返回true。相反,如果任何字符无效,则立即返回false。

2)您可能处理了太多字符

第二,您的循环正好检查10个字符。如果您输入的字符串较短,strcpy()将在末尾加上'\0'来标记c字符串的结尾(此字符与任何有效的罗马字符都不匹配),并保留其余字符未初始化(因此包含垃圾,也很可能不是罗马)。

解决方案:确保在到达c字符串的末尾时,循环条件为false。

3)如果输入字符串太长会怎样?

第三,strcpy()不安全。如果您输入的字符串的长度为9个字符,strcpy()会将10个字符复制到其目标中(由于结尾的'\0'终止符)。不幸的是,如果您输入的字符串更长,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;
} 

Online demo

但这是旧版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个字符,没问题:-)它将检查所有字符!

要获得更好的解决方案,您现在可以查看其他答案

答案 3 :(得分:0)

这里是一个简单的替代方法,它不使用任何库函数来检查罗马数字。只是一个循环和一个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