Miałem spory problem z napisaniem funkcji, która sprawdza, czy ciąg jest liczbą. W przypadku gry, którą piszę, muszę tylko sprawdzić, czy wiersz z czytanego pliku jest liczbą, czy nie (będę wiedział, czy jest to parametr w ten sposób). Napisałem poniższą funkcję, która moim zdaniem działała płynnie (lub przypadkowo wyedytowałem, aby ją zatrzymać, albo jestem schizofrenikiem lub Windows jest schizofrenikiem):
bool isParam (string line)
{
if (isdigit(atoi(line.c_str())))
return true;
return false;
}
c++
visual-c++
Brendan Weinstein
źródło
źródło
if (expr) return true; return false;
! Po prostu napiszreturn expr;
.if (expr) return expr; else return expr;
,if (expr == true)
,(if expr != false)
, lubif ((expr == true) == true)
. Wszystkie wprowadzają złożoność, która nie jest korzystna dla autora, czytelnika ani kompilatora kodu. Eliminacja zbędnej złożoności nie jest drogą na skróty; to klucz do tworzenia lepszego oprogramowania.Odpowiedzi:
Najbardziej wydajnym sposobem byłoby po prostu iterowanie po ciągu, aż znajdziesz znak niebędący cyfrą. Jeśli są jakieś znaki niebędące cyframi, możesz uznać, że ciąg nie jest liczbą.
bool is_number(const std::string& s) { std::string::const_iterator it = s.begin(); while (it != s.end() && std::isdigit(*it)) ++it; return !s.empty() && it == s.end(); }
Lub jeśli chcesz to zrobić w C ++ 11 sposób:
bool is_number(const std::string& s) { return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return !std::isdigit(c); }) == s.end(); }
Jak wskazano w komentarzach poniżej, działa to tylko dla dodatnich liczb całkowitych. Jeśli chcesz wykryć ujemne liczby całkowite lub ułamki, powinieneś wybrać bardziej niezawodne rozwiązanie oparte na bibliotekach. Chociaż dodanie obsługi ujemnych liczb całkowitych jest dość trywialne.
źródło
!s.empty() && s.find_first_not_of("0123456789") == std::string::npos;
do jednowierszowego C ++ 03.int
. Po prostu identyfikuje, czy ciąg składa się z cyfr numerycznych. Nie ma znaczenia, jak długi jest sznurek.<string>
<algorithm>
i<cctype>
sprawić, by przykład C ++ 11 zadziałał.Po co odkrywać koło na nowo? Biblioteka standardowa C (dostępna również w C ++) ma funkcję, która robi dokładnie to:
char* p; long converted = strtol(s, &p, 10); if (*p) { // conversion failed because the input wasn't a number } else { // use converted }
Jeśli chcesz obsługiwać ułamki lub notację naukową, użyj
strtod
zamiast tego (otrzymaszdouble
wynik).Jeśli chcesz zezwolić na stałe szesnastkowe i ósemkowe w C / C ++ style (
"0xABC"
),0
zamiast tego utwórz ostatni parametr .Twoja funkcja może być zapisana jako
bool isParam(string line) { char* p; strtol(line.c_str(), &p, 10); return *p == 0; }
źródło
atoi
użyte w pytaniu również to robi).p
zostanie ustawione,nullptr
jeślistrtol
się powiedzie, prawda? To nie to, co widzę :(p
będzie wskazywać na NUL, który kończy ciąg. Więcp != 0
i*p == 0
.W przypadku kompilatora C ++ 11 dla nieujemnych liczb całkowitych użyłbym czegoś takiego (zwróć uwagę na
::
zamiaststd::
):bool is_number(const std::string &s) { return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit); }
http://ideone.com/OjVJWh
źródło
Możesz to zrobić w C ++ za pomocą boost :: lexical_cast. Jeśli naprawdę nalegasz, aby nie używać boostu, możesz po prostu sprawdzić, co robi i to zrobić. To całkiem proste.
try { double x = boost::lexical_cast<double>(str); // double could be anything with >> operator. } catch(...) { oops, not a number }
źródło
try{} catch{}
z dobrego pomysłu? Czy nie powinniśmy tego unikać tak bardzo, jak to tylko możliwe?Chciałem tylko wrzucić ten pomysł, który używa iteracji, ale inny kod robi tę iterację:
#include <string.h> bool is_number(const std::string& s) { return( strspn( s.c_str(), "-.0123456789" ) == s.size() ); }
Nie jest tak solidna, jak powinna być podczas sprawdzania kropki dziesiętnej lub znaku minus, ponieważ pozwala na istnienie więcej niż jednego z nich i w dowolnej lokalizacji. Dobrą rzeczą jest to, że jest to pojedynczy wiersz kodu i nie wymaga biblioteki innej firmy.
Wyjmij „.” i „-”, jeśli dozwolone są tylko dodatnie liczby całkowite.
źródło
std::string
, użyj jegofind_first_not_of
funkcji członkowskiej.Sugerowałbym podejście regex. Pełne dopasowanie wyrażenia regularnego (na przykład przy użyciu boost :: regex ) z
-?[0-9]+([\.][0-9]+)?
pokaże, czy ciąg jest liczbą, czy nie. Obejmuje to liczby dodatnie i ujemne, zarówno całkowite, jak i dziesiętne.
Inne odmiany:
[0-9]+([\.][0-9]+)?
(tylko pozytywne)
-?[0-9]+
(tylko liczba całkowita)
[0-9]+
(tylko dodatnia liczba całkowita)
źródło
std::regex
z gcc 4.7, gcc 4.8 - oba rzucająstd::regex_error
na dowolny znak[
w regexp, nawet dla niewinnego "[abc]" (czy robię to źle?). clang-3.4 w ogóle nie jest tego świadomy<regex>
. W każdym razie wydaje się, że to najbardziej rozsądna odpowiedź, +1.Oto inny sposób na zrobienie tego za pomocą
<regex>
biblioteki:bool is_integer(const std::string & s){ return std::regex_match(s, std::regex("[(-|+)|][0-9]+")); }
źródło
Dzięki temu rozwiązaniu możesz sprawdzić wszystko, od liczb ujemnych do dodatnich, a nawet liczb zmiennoprzecinkowych. Gdy zmienisz typ
num
na liczbę całkowitą, pojawi się błąd, jeśli łańcuch zawiera punkt.#include<iostream> #include<sstream> using namespace std; int main() { string s; cin >> s; stringstream ss; ss << s; float num = 0; ss >> num; if(ss.good()) { cerr << "No Valid Number" << endl; } else if(num == 0 && s[0] != '0') { cerr << "No Valid Number" << endl; } else { cout << num<< endl; } }
Udowodnij: program w C ++
źródło
Uważam, że następujący kod jest najbardziej niezawodny (C ++ 11). Przechwytuje zarówno liczby całkowite, jak i zmiennoprzecinkowe.
#include <regex> bool isNumber( std::string token ) { return std::regex_match( token, std::regex( ( "((\\+|-)?[[:digit:]]+)(\\.(([[:digit:]]+)?))?" ) ) ); }
źródło
using namespace std;
jest niepotrzebna.Spróbuj tego:
isNumber(const std::string &str) { return !str.empty() && str.find_first_not_of("0123456789") == string::npos; }
źródło
Oto rozwiązanie sprawdzania dodatnich liczb całkowitych:
bool isPositiveInteger(const std::string& s) { return !s.empty() && (std::count_if(s.begin(), s.end(), std::isdigit) == s.size()); }
źródło
Brendan to
bool isNumber(string line) { return (atoi(line.c_str())); }
jest prawie w porządku.
zakładając, że każdy ciąg zaczynający się od 0 jest liczbą, po prostu dodaj sprawdzenie dla tego przypadku
bool isNumber(const string &line) { if (line[0] == '0') return true; return (atoi(line.c_str())); }
ofc "123hello" zwróci prawdę, jak zauważył Tony D.
źródło
Najprostszy, jaki przychodzi mi do głowy w c ++
bool isNumber(string s) { if(s.size()==0) return false; for(int i=0;i<s.size();i++) { if((s[i]>='0' && s[i]<='9')==false) { return false; } } return true; }
Przykład kodu roboczego: https://ideone.com/nRX51Y
źródło
Moje rozwiązanie wykorzystujące C ++ 11 regex (
#include <regex>
), może być użyte do dokładniejszego sprawdzenia, na przykładunsigned int
,double
itp:static const std::regex INT_TYPE("[+-]?[0-9]+"); static const std::regex UNSIGNED_INT_TYPE("[+]?[0-9]+"); static const std::regex DOUBLE_TYPE("[+-]?[0-9]+[.]?[0-9]+"); static const std::regex UNSIGNED_DOUBLE_TYPE("[+]?[0-9]+[.]?[0-9]+"); bool isIntegerType(const std::string& str_) { return std::regex_match(str_, INT_TYPE); } bool isUnsignedIntegerType(const std::string& str_) { return std::regex_match(str_, UNSIGNED_INT_TYPE); } bool isDoubleType(const std::string& str_) { return std::regex_match(str_, DOUBLE_TYPE); } bool isUnsignedDoubleType(const std::string& str_) { return std::regex_match(str_, UNSIGNED_DOUBLE_TYPE); }
Możesz znaleźć ten kod na http://ideone.com/lyDtfi , można go łatwo zmodyfikować, aby spełniał wymagania.
źródło
Jak zostało mi ujawnione w odpowiedzi na moje pokrewne pytanie, uważam, że powinieneś użyć boost :: conversion :: try_lexical_convert
źródło
Możemy użyć klasy stringstream .
bool isNumeric(string str) { stringstream stream; double number; stream<<str; stream>>number; return stream.eof(); }
źródło
Rozwiązanie oparte na komentarzu kbjorklu to:
bool isNumber(const std::string& s) { return !s.empty() && s.find_first_not_of("-.0123456789") == std::string::npos; }
Podobnie jak w przypadku odpowiedzi Davida Rectora, nie jest ona odporna na ciągi z wieloma kropkami lub znakami minus, ale możesz usunąć te znaki, aby po prostu sprawdzić liczby całkowite.
Jednak jestem zwolennikiem rozwiązania opartego na rozwiązaniu Bena Voigta , używającego
strtod
w cstdlib do wyszukiwania wartości dziesiętnych, notacji naukowej / inżynierskiej, notacji szesnastkowej (C ++ 11), a nawet INF / INFINITY / NAN (C ++ 11) jest:bool isNumberC(const std::string& s) { char* p; strtod(s.c_str(), &p); return *p == 0; }
źródło
Korzystanie
<regex>
. Ten kod został przetestowany!bool isNumber(const std::string &token) { return std::regex_match(token, std::regex("(\\+|-)?[0-9]*(\\.?([0-9]+))$")); }
źródło
Po dokładniejszym zapoznaniu się z dokumentacją, znalazłem odpowiedź, która wspiera moje potrzeby, ale prawdopodobnie nie będzie tak pomocna dla innych. Oto jest (bez irytującego powrotu true i zwrócenia fałszywych stwierdzeń :-))
bool isNumber(string line) { return (atoi(line.c_str())); }
źródło
0
, otrzymasz wynik fałszywie ujemny.Myślę, że to wyrażenie regularne powinno obsługiwać prawie wszystkie przypadki
"^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"
więc możesz wypróbować następującą funkcję, która może działać zarówno z (Unicode, jak i ANSI)
bool IsNumber(CString Cs){ Cs.Trim(); #ifdef _UNICODE std::wstring sr = (LPCWSTR)Cs.GetBuffer(Cs.GetLength()); return std::regex_match(sr, std::wregex(_T("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?"))); #else std::string s = (LPCSTR)Cs.GetBuffer(); return std::regex_match(s, std::regex("^(\\-|\\+)?[0-9]*(\\.[0-9]+)?")); #endif }
źródło
include <string>
W przypadku walidacji podwójnych:
bool validateDouble(const std::string & input) { int decimals = std::count(input.begin(), input.end(), '.'); // The number of decimals in the string int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string if (input.size() == decimals + negativeSigns) // Consists of only decimals and negatives or is empty return false; else if (1 < decimals || 1 < negativeSigns) // More than 1 decimal or negative sign return false; else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character return false; else if (strspn(input.c_str(), "-.0123456789") != input.size()) // The string contains a character that isn't in "-.0123456789" return false; return true;
}
Do walidacji Ints (z negatywami)
bool validateInt(const std::string & input) { int negativeSigns = std::count(input.begin(), input.end(), '-'); // The number of negative signs in the string if (input.size() == negativeSigns) // Consists of only negatives or is empty return false; else if (1 < negativeSigns) // More than 1 negative sign return false; else if (1 == negativeSigns && input[0] != '-') // The negative sign (if there is one) is not the first character return false; else if (strspn(input.c_str(), "-0123456789") != input.size()) // The string contains a character that isn't in "-0123456789" return false; return true;
}
Do weryfikacji niepodpisanych stron
bool validateUnsignedInt(const std::string & input) { return (input.size() != 0 && strspn(input.c_str(), "0123456789") == input.size()); // The string is not empty and contains characters only in "0123456789"
}
źródło
bool isNumeric(string s){ if ( !s.empty() && s[0] != '-' ) s = "0" + s; //prepend 0 string garbage; stringstream ss(s); ss >> *(auto_ptr<double>(new double)) >> garbage; /* //the line above extracts the number into an anonymous variable. it could also be done like this: double x; ss >> x >> garbage; */ //if there is no garbage return true or else return false return garbage.empty(); }
jak to działa: stringstream >> overload może konwertować łańcuchy do różnych typów arytmetycznych, robi to poprzez sekwencyjne odczytywanie znaków ze strumienia (w tym przypadku ss), dopóki nie zabraknie znaków LUB następny znak nie spełnia kryteriów do zapisania do typu zmiennej docelowej.
Przykład 1:
stringstream ss("11"); double my_number; ss >> my_number; //my number = 11
przykład2:
stringstream ss("011"); double my_number; ss >> my_number; //my number = 11
przykład3:
stringstream ss("11ABCD"); double my_number; ss >> my_number; //my number = 11 (even though there are letters after the 11)
objaśnienie zmiennej „śmieci”:
dlaczego nie po prostu sprawdzić, czy wyodrębnienie do mojego double ma prawidłową wartość, a następnie zwrócić true, jeśli tak?
Zauważ, że przykład3 powyżej nadal pomyślnie odczyta liczbę 11 do zmiennej my_number, nawet jeśli ciąg wejściowy to „11ABCD” (który nie jest liczbą).
aby obsłużyć ten przypadek, możemy wykonać kolejne wyodrębnienie do zmiennej łańcuchowej (którą nazwałem śmieci), która może odczytać wszystko, co mogło zostać w buforze ciągów po początkowym wyodrębnieniu do zmiennej typu double. Jeśli cokolwiek zostanie, zostanie wczytane do „śmieci”, co oznacza, że cały przekazany ciąg nie był liczbą (po prostu zaczyna się od jedynki). w takim przypadku chcielibyśmy zwrócić fałsz;
dołączone „0” wyjaśnienie ”:
próba wyodrębnienia pojedynczego znaku do znaku podwójnego zakończy się niepowodzeniem (zwrócenie 0 do naszego znaku podwójnego), ale nadal spowoduje przesunięcie pozycji bufora ciągu na koniec znaku. W takim przypadku nasz odczyt śmieci będzie pusty, co spowoduje, że funkcja niepoprawnie zwróci wartość true. aby obejść ten problem, dodałem 0 do łańcucha, więc jeśli na przykład przekazany ciąg był „a”, zostanie zmieniony na „0a”, tak że 0 zostanie wyodrębnione do podwójnego, a „a” zostanie wyodrębnione do śmieci.
dodanie 0 nie wpłynie na wartość liczby, więc liczba będzie nadal poprawnie wyodrębniona do naszej podwójnej zmiennej.
źródło
aby sprawdzić, czy łańcuch jest liczbą całkowitą, czy zmiennoprzecinkową, możesz użyć:
#include <sstream> bool isNumber(string str) { double d; istringstream is(str); is >> d; return !is.fail() && is.eof(); }
źródło
Jeszcze inna odpowiedź, która używa
stold
(choć możesz też użyćstof
/stod
jeśli nie potrzebujesz precyzji).bool isNumeric(const std::string& string) { std::size_t pos; long double value = 0.0; try { value = std::stold(string, &pos); } catch(std::invalid_argument&) { return false; } catch(std::out_of_range&) { return false; } return pos == string.size() && !std::isnan(value); }
źródło
Spróbuj tego:
bool checkDigit(string str) { int n=str.length(); for(int i=0; i < n ; i++) { if(str[i]<'0' || str[i]>'9') return false; } return true; }
źródło
Możesz sprawdzić, czy ciąg można zamienić na liczbę całkowitą, używając boost :: lexical_cast . Jeśli zgłosi wyjątek bad_lexical_cast, to łańcuch nie może zostać przekonwertowany, w przeciwnym razie może.
Zobacz przykład takiego programu testowego poniżej:
#include <boost/lexical_cast.hpp> #include <iostream> int main(int, char** argv) { try { int x = boost::lexical_cast<int>(argv[1]); std::cout << x << " YES\n"; } catch (boost::bad_lexical_cast const &) { std:: cout << "NO\n"; } return 0; }
Przykładowe wykonanie:
# ./a.out 12 12 YES # ./a.out 12/3 NO
źródło
Kilka miesięcy temu zaimplementowałem sposób określania, czy dowolny ciąg jest liczbą całkowitą, szesnastkową czy podwójną.
enum{ STRING_IS_INVALID_NUMBER=0, STRING_IS_HEXA, STRING_IS_INT, STRING_IS_DOUBLE }; bool isDigit(char c){ return (('0' <= c) && (c<='9')); } bool isHexaDigit(char c){ return ((('0' <= c) && (c<='9')) || ((tolower(c)<='a')&&(tolower(c)<='f'))); } char *ADVANCE_DIGITS(char *aux_p){ while(CString::isDigit(*aux_p)) aux_p++; return aux_p; } char *ADVANCE_HEXADIGITS(char *aux_p){ while(CString::isHexaDigit(*aux_p)) aux_p++; return aux_p; } int isNumber(const string & test_str_number){ bool isHexa=false; char *str = (char *)test_str_number.c_str(); switch(*str){ case '-': str++; // is negative number ... break; case '0': if(tolower(*str+1)=='x') { isHexa = true; str+=2; } break; default: break; }; char *start_str = str; // saves start position... if(isHexa) { // candidate to hexa ... str = ADVANCE_HEXADIGITS(str); if(str == start_str) return STRING_IS_INVALID_NUMBER; if(*str == ' ' || *str == 0) return STRING_IS_HEXA; }else{ // test if integer or float str = ADVANCE_DIGITS(str); if(*str=='.') { // is candidate to double str++; str = ADVANCE_DIGITS(str); if(*str == ' ' || *str == 0) return STRING_IS_DOUBLE; return STRING_IS_INVALID_NUMBER; } if(*str == ' ' || *str == 0) return STRING_IS_INT; } return STRING_IS_INVALID_NUMBER; }
Następnie w swoim programie możesz łatwo przekonwertować liczbę w funkcji na jej typ, jeśli wykonasz następujące czynności,
string val; // the string to check if number... switch(isNumber(val)){ case STRING_IS_HEXA: // use strtol(val.c_str(), NULL, 16); to convert it into conventional hexadecimal break; case STRING_IS_INT: // use (int)strtol(val.c_str(), NULL, 10); to convert it into conventional integer break; case STRING_IS_DOUBLE: // use atof(val.c_str()); to convert it into conventional float/double break; }
Możesz zdać sobie sprawę, że funkcja zwróci 0, jeśli liczba nie została wykryta. Wartość 0 może być traktowana jako fałszywa (jak wartość logiczna).
źródło
Proponuję prostą konwencję:
Jeśli konwersja do ASCII jest> 0 lub zaczyna się od 0, to jest to liczba. Nie jest doskonały, ale szybki.
Coś takiego:
string token0; if (atoi(token0.c_str())>0 || isdigit(token0.c_str()[0]) ) { //this is a value // do what you need to do... }
źródło
Ta funkcja zajmuje się wszystkimi możliwymi przypadkami:
bool AppUtilities::checkStringIsNumber(std::string s){ //Eliminate obvious irritants that could spoil the party //Handle special cases here, e.g. return true for "+", "-", "" if they are acceptable as numbers to you if (s == "" || s == "." || s == "+" || s == "-" || s == "+." || s == "-.") return false; //Remove leading / trailing spaces **IF** they are acceptable to you while (s.size() > 0 && s[0] == ' ') s = s.substr(1, s.size() - 1); while (s.size() > 0 && s[s.size() - 1] == ' ') s = s.substr(0, s.size() - 1); //Remove any leading + or - sign if (s[0] == '+' || s[0] == '-') s = s.substr(1, s.size() - 1); //Remove decimal points long prevLength = s.size(); size_t start_pos = 0; while((start_pos = s.find(".", start_pos)) != std::string::npos) s.replace(start_pos, 1, ""); //If the string had more than 2 decimal points, return false. if (prevLength > s.size() + 1) return false; //Check that you are left with numbers only!! //Courtesy selected answer by Charles Salvia above std::string::const_iterator it = s.begin(); while (it != s.end() && std::isdigit(*it)) ++it; return !s.empty() && it == s.end(); //Tada.... }
źródło
Czy możesz po prostu użyć kodu powrotu sscanf, aby określić, czy jest to int?
bool is_number(const std::string& s) { int value; int result = sscanf(valueStr.c_str(), "%d", &value); return (result != EOF && readResult != 0); }
źródło