多输入验证约束? C ++

时间:2013-12-19 20:50:51

标签: c++ validation input

我必须确保输入的字母仅由 M,D,C,L,X,V和I 组成,我已经完成了。

我的问题是它们是罗马数字,我还必须确保如果较低值字符位于较高值字符之前,则它遵循罗马数字的规则:

  • M,D,C 前面可以加2个字符,因此CMDM可以,但LM不是。
  • L,X,V 前面只能加一个字符1点,因此XL可以,但VL不是。

7个字母代表如下数值:

╔════════╦═══════╗
║ Symbol ║ Value ║
╠════════╬═══════╣
║ I      ║ 1     ║
║ V      ║ 5     ║
║ X      ║ 10    ║
║ L      ║ 50    ║
║ C      ║ 100   ║
║ D      ║ 500   ║
║ M      ║ 1,000 ║
╚════════╩═══════╝

这就是我到目前为止:

void romanType::storeRoman()
{
    locale loc;
    bool valid = true;

    do
    {
        valid = true;
        cout << "please enter a roman numeral using no spaces:" << endl;
        getline(cin, rNums);

        for (i = 0; i < rNums.length(); ++i)
        {
            rNums[i] = toupper(rNums[i], loc);
            if (rNums[i] == 'M' || rNums[i] == 'D' || rNums[i] == 'C' ||
                rNums[i] == 'L' || rNums[i] == 'X' || rNums[i] == 'V' ||
                rNums[i] == 'I')
            {
                continue;
            }
            valid = false;
        }

        cout << "input error please try again\n" << endl;
    } while (!valid);

    /* ... */

}

只要所有字符都是罗马数字,它就可以工作,但我不能为我的生活弄清楚如何实现我提到的其他2个约束。我已经完成了大部分程序的其余部分,但我花了大约6到7个小时试图让这一部分工作。请帮忙。

2 个答案:

答案 0 :(得分:2)

遵循您的规则,有效罗马数 MCMLIV(1954)将被视为无效,例如C前面不能ML前面不能M I。因此,您的规则是错误的或不完整的。

但是,如果您改为使用这些规则(取自Wikipedia),那么它将起作用:

  • 数字V可以放在XIV之前,分别制作4个单位(IX)和9个单位(X
  • L可以放在CXL之前分别制作40(XC)和90(C
  • D可以放在MCD之前,根据相同的模式制作400(CM)和900(IVXLCDM)。

现在,你的功能基本上需要执行两件事:

  1. 检查无效字符(仅允许// The string representing a roman number. std::string s = "MCMLIV"; // Check that the predicate is true for all elements in range. const std::string allowedChars = "IVXLCDM"; bool valid = std::all_of(std::begin(s), std::end(s), [&allowedChars] (char c) { return allowedChars.find(::toupper(c)) != std::string::npos; }); if (!valid) { std::cerr << "Input error" << std::endl; return EXIT_FAILURE; }
  2. 检查以上规则是否适用。
  3. 您的功能是第一个,但它可以简化。我们要做的就是在字符串中找到任何无效字符。我们可以使用std::all_of检查所有字符是否有效。

    // Check if we can find any pair that does not comply to the rules.
    auto it = std::adjacent_find(std::begin(s), std::end(s), [] (char lhs, char rhs)
    {
        lhs = std::toupper(lhs); // Transform to upper case.
        rhs = std::toupper(rhs); // Transform to upper case.
        return ((lhs == 'I' && rhs != 'V') && (lhs == 'I' && rhs != 'X')) ||
               ((lhs == 'X' && rhs != 'L') && (lhs == 'X' && rhs != 'C')) ||
               ((lhs == 'C' && rhs != 'D') && (lhs == 'C' && rhs != 'M'));
    });
    
    if (it != std::end(s)) {
        std::cerr << "Input error" << std::endl;
        return EXIT_FAILURE;
    }
    

    然后我们需要根据规则成对检查字符。我们可以使用std::adjacent_find

    I

    就是这样!请参阅此live example

    修改

    您还必须检查IIIIIII连续发生的次数是否超过3次,因此不要接受{{1}}字符串。这应该相当容易。

答案 1 :(得分:0)

一种方法(来自C心态)可能是列出合法字符,以及与之对应的合法“抵消”(定义如下):

#define LEGALCHAR 7 // number of possible legal letters in roman numerals   
char legalValues[LEGALCHAR] = {'I', 'V', 'X', 'L', 'C', 'D', 'M'};
int allowedDelta[LEGALCHAR] = {0, 1, 1, 1, 2, 2, 2};

并创建一个int IndexedNumeral数组,其中包含来自匹配字符的legalValues的索引,例如MMX将是{6,6,2}。然后循环遍历IndexedNumeral的第2个到最后一个条目,以比较i-1i个字符与i个字符允许的合法偏移之间的差异。

diff = indexedNumeral[i] - indexedNumeral[i-1];
if (diff > allowedDelta[indexedNumeral[i]]) {
  printf("illegal sequence: %i %i or  %c %c\n", indexedNumeral[i-1], indexedNumeral[i], argv[1][i-1], argv[1][i]);
  return 1;
}

当然还有其他方法。