Я разработал код, чтобы ограничить SpinBox буквами вместо целых чисел. Все работает нормально, но я бы хотел сократить операторы if-elseif, если есть какой-нибудь умный способ. Это код
std::string AlphaSpinBox::textFromValue(int value)
{
// I feel the code is Ok but willing to change it if there is a better way.
// value is restricted [0-25] inclusive.
std::string str("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
return std::string(str[value]);
}
int AlphaSpinBox::valueFromText(std::string &text)
{
// can I shorten the following?!
// text is solely one letter (i.e. either upper or lower)
if(text == 'A' || text == 'a')
return 0;
else if(text == 'B' || text == 'b' )
return 1;
else if(text == 'C' || text == 'c')
return 2;
else if(text == 'D' || text == 'd')
return 3;
... to z letter
}
Как насчет:
if (text.size()>0 && std::isalpha(text[0]))
return std::toupper(text[0])-'A';
else return -1; // or throw an exception
Здесь онлайн демо.
Как это работает: сначала проверяется, не является ли строка пустой, и является ли первый символ буквой. (с isalpha()
). Если это верно, так как вы не делаете разницы между строчными и прописными буквами, мы конвертируем символ toupper()
. Поскольку ваши возвращаемые значения являются последовательными в порядке буквы, мы просто должны вычесть первую букву.
Каждая буква имеет целочисленное значение в соответствии с таблицей ASCII. Если вы посмотрите на это, вы также обнаружите, что буквы были удобно размещены в таблице: все буквы от a до z следуют друг за другом напрямую, то же самое от A до Z.
Вы можете сначала определить строчные или прописные буквы, а затем вернуть text[0] - 'a'
, или же text[0] - 'b'
,
Вы можете устранить несколько if
заявления в вашем valueFromText
функция с помощью строки находить Функция-член и незначительное изменение сигнатуры функции:
int valueFromText(const std::string& s, char c) {
return s.find(std::toupper(static_cast<unsigned char>(c)));
}
Ниже, может быть, более понятный:
int AlphaToNumeric(string &value)
{
return (value.front() >= 'A' && value.front() <= 'Z') ? value.front() - 'A' : (value.front() >= 'a' && value.front() <= 'z') ? value.front() - 'a' : -1;
}
Или же:
int AlphaToNumeric(string &value)
{
return (value.front() >= 65 && value.front() <= 90) ? value.front() - 65 : (value.front() >= 97 && value.front() <= 122) ? value.front() - 97 : -1;
}
Не уверен, что это квалифицирует как «умный», но при условии, что вы хотите посмотреть только на первый символ text
можно просто сделать
#include <string>
#include <cctype>
int AlphaSpinBox::valueFromText(const std::string &text)
{
std::string str("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
int retval = -2;
if (text.size() > 0)
{
char c = std::toupper(text[0]);
std::size_t index = str.find(c);
retval = (index != std::string::npos) ? int(index) : -1;
}
return retval;
}
В отличие от кода в исходном вопросе, он будет скомпилирован (поскольку он пытается преобразовать первый символ в строке, а не всю строку). Тем не менее, он также делает больше проверок, поэтому вернется -2
если дана строка нулевой длины, -1
если первый символ в строке не алфавитный.
Если вы ПРИНИМАЕТЕ набор символов, в котором (заглавные) буквы встречаются последовательно, это можно сделать намного проще. Это предположение не совсем верно для всех стандартизированных наборов символов, но чаще всего верно для современных систем.
#include <string>
#include <cctype>
int AlphaSpinBox::valueFromText(const std::string &text)
{
int retval = -2;
if (text.size() > 0)
{
int c = std::toupper(text[0]);
retval = std::isupper(c) ? c - 'A' : -1;
}
return retval;
}
Выбор использовать одну из этих версий или другую зависит от степени уверенности в том, что код никогда не будет перенесен в систему с набором символов, в котором буквы являются непоследовательными.
Написание небольшого класса или структуры со статическим членом (std::map
— look up table
) & некоторые статические методы, которые действуют как обертка, будут определенно работать в вашей ситуации. Использование кода достаточно чистое, читаемое, простое в использовании, должно быть переносимым и многоразовым.
Замечания: — Это будет работать при условии, что коды символов системы определены последовательно; в противном случае вам понадобится другой механизм для initialize
статическая карта.
AlphaSpinBox.h
#ifndef ALPHA_SPIN_BOX_H
#define ALPHA_SPIN_BOX_H
#include <string>
#include <cctype>
#include <map>
struct AlphaSpinBox {
// static table
static std::map<unsigned, std::string> table_;
// Must be called first
static void initializeMap();
// helper function
static std::string toUpper( const std::string& str );
// get string from value
static std::string textFromValue( const unsigned& val );
// get value from string
static unsigned valueFromText( const std::string& text );
// other member's, functions etc. that you may have for this class
};
#endif // !ALPHA_SPIN_BOX_H
AlphaSpinBox.cpp
#include "AlphaSpinBox.h"
// define static member
std::map<unsigned, std::string> AlphaSpinBox::table_;
void AlphaSpinBox::initializeMap() {
// Could do some checks here to see if this function has not been called
// then display a message to the user that this function needs to be called first;
// and to check if it has already been called once before; warning the user
// that this method should only initialize the map once per application run.
static char c = 'A';
static std::string str;
for ( unsigned n = 0; n < 26; n++ ) {
str.assign( &c );
table_.insert( std::make_pair( n, str ) );
c++;
}
}
std::string AlphaSpinBox::toUpper( const std::string& str ) {
std::string result = str;
std::transform( str.begin(), str.end(), result.begin(), ::toupper );
return result;
}
std::string AlphaSpinBox::textFromValue( const unsigned& val ) {
// you could check to see if val is within range before returning...
return table_[val];
}
unsigned AlphaSpinBox::valueFromText( const std::string& text ) {
std::string upper = toUpper( text );
for ( auto pair : table_ ) {
if ( upper == pair.second ) {
return pair.first;
}
}
return -1;
}
main.cpp
#include <string>
#include <iostream>
#include "AlphaSpinBox.h"
int main() {
// Must Be Called First
AlphaSpinBox::initializeMap();
// Remember that the map first entry's key starts at 0 and not 1
std::cout << "The number 8 has letter: "<< AlphaSpinBox::textFromValue( 8 )
<< std::endl;
std::cout << "The letter Q has value: "<< AlphaSpinBox::valueFromText( std::string( "Q" ) )
<< std::endl;
// check case for lower cases being converted to upper case
std::cout << "The letter j has value: "<< AlphaSpinBox::valueFromText( std::string( "j" ) )
<< std::endl;
std::cout << "\nPress any key and enter to quit." << std::endl;
char q;
std::cin >> q;
return 0;
}