Превращение строки в символ и проверка букв - PullRequest
0 голосов
/ 01 сентября 2018

У меня возникли некоторые проблемы с частью моего кода, я думаю, что это вызывает ошибку, когда я пытаюсь проверить его с введенным вводом, что приводит к зависанию 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 ++, поэтому не могу сказать, что я делаю неправильно, поэтому предложения крайне приветствуются.

Ответы [ 4 ]

0 голосов
/ 02 сентября 2018

Здесь есть множество отличных решений. Однако я хотел бы объяснить, почему ваш код не работает, потому что эти ошибки усложнят вашу жизнь, если вы не научитесь их избегать.

1) Логическая ошибка в цикле

Во-первых, ваш цикл будет проверять только первый символ строки: первое выполнение предложения if в цикле обязательно приведет к return, без проверки других символов!

Решение: проверить каждый символ и вернуть true только после того, как цикл успешно проверит их все. Напротив, если какой-либо символ недопустим, немедленно верните false.

2) Вы можете обработать слишком много символов

Во-вторых, ваш цикл проверяет ровно 10 символов. Если ваша входная строка короче, strcpy() введет завершающий '\0', чтобы отметить конец c-строки (и этот символ не соответствует ни одному действительному римскому), и оставит оставшиеся символы неинициализированными (таким образом, содержащий фигня, скорее всего не римская тоже).

Решение: убедитесь, что условие цикла ложно, когда достигнут конец c-строки.

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

Демоверсия

Но это устаревший 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 символов :-) Она проверит их все!

Для еще лучших решений, теперь вы можете посмотреть на другие ответы

0 голосов
/ 02 сентября 2018

Просто убедитесь, что недопустимые цифры не могут быть найдены:

bool romanDigitsOnly(const std::string& number) {
  return number.find_first_not_of("IVXLCDM") == std::string::npos;
}

find_first_not_of возвращает npos, если ни один из указанных символов не найден в строке. Обратите внимание, это проверяет только действительные цифры, а не действительные римские цифры .

0 голосов
/ 02 сентября 2018

Вот простая альтернатива, которая проверяет римские цифры, не используя библиотечные функции. Просто цикл и оператор 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
0 голосов
/ 02 сентября 2018

Вам не нужно копировать содержимое 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...