【问题标题】:multiple input validation constraints? c++多个输入验证约束? C++
【发布时间】:2013-12-19 20:50:51
【问题描述】:

我必须确保输入的字母仅包含 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 个小时试图让这一部分也能正常工作。请帮忙。

【问题讨论】:

  • 请以人们可以阅读的方式格式化您的代码。完全随机格式化似乎是所有者不理解的代码的共同特征。
  • 好吧,把定义好的数字字符按正确的顺序放在一个数组中,然后匹配你输入的字符,并根据索引做一些相对比较。
  • 这是一些很奇怪的要求,会不会让某些数字无法输入?
  • 哈,我没想到 VL 是不正确的(而 XLV 似乎是“正确”的选择。)
  • 对哞哞鸭:不,它们就是这样工作的。

标签: c++ validation input


【解决方案1】:

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

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

  • 数字I可以放在VX之前分别构成4个单元(IV)和9个单元(IX
  • X 可以放在 LC 之前,分别为 40 (XL) 和 90 (XC)
  • C 可以放在 DM 之前,按照相同的模式组成 400 (CD) 和 900 (CM)。

现在,您的函数基本上需要执行两件事:

  1. 检查无效字符(只允许IVXLCDM
  2. 检查上述规则是否适用。

你的函数是第一个,但它可以被简化。我们要做的就是找到字符串中的任何无效字符。我们可以使用std::all_of检查所有字符是否有效。

// 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;
}

然后我们需要按照规则成对地检查字符。我们可以为此使用std::adjacent_find

// 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;
}

就是这样!看到这个live example

编辑:

您还必须检查 I 连续出现的次数不超过 3 次,因此不要接受字符串为 IIIIIII。不过这应该很容易。

【讨论】:

  • 我认为这些规则是不完整的,不应接受 IIIIIIIIIIIIIIII。
  • @ChristopherCreutzig 规则说“制造 4 个单元 (IV) 和 9 个单元 (IX)”,这可以解释为对允许的 I 数量的限制。不过,我添加了一个关于它的编辑,感谢您指出。
【解决方案2】:

一种方法(从 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 的第二个到最后一个条目,以比较第(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;
}

当然还有其他方法。

【讨论】:

    猜你喜欢
    • 2018-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-13
    • 2013-11-15
    • 2010-09-22
    • 2019-06-08
    • 1970-01-01
    相关资源
    最近更新 更多