2009-08-24 18 views
27

Zdaję sobie sprawę, że to pytanie mogło być zadawane kilka razy w przeszłości, ale zamierzam kontynuować niezależnie.Konwertuj ciąg zawierający kilka liczb na liczby całkowite

Mam program, który otrzyma ciąg liczb z klawiatury. Liczby zawsze będą miały postać "66 33 9". Zasadniczo każda liczba jest oddzielona spacją, a dane wprowadzane przez użytkownika zawsze zawierają inną liczbę liczb.

Jestem świadomy, że użycie "sscanf" zadziała, jeśli ilość liczb w każdym ciągu wprowadzonym przez użytkownika będzie stała, ale nie dotyczy to mnie. Ponadto, ponieważ jestem nowy w C++, wolę radzić sobie ze zmiennymi "string" niż z tablicami znaków.

+6

IMO na ogół preferuje 'std :: stri Odtąd od buforów surowych znaków nie jest znakiem "newbieness", ale raczej dojrzałości. – sbi

Odpowiedz

29

Zakładam, że chcesz przeczytać cały wiersz i przeanalizować go jako dane wejściowe. Więc najpierw chwycić wiersz:

std::string input; 
std::getline(std::cin, input); 

Teraz umieścić że w stringstream:

std::stringstream stream(input); 

i analizować

while(1) { 
    int n; 
    stream >> n; 
    if(!stream) 
     break; 
    std::cout << "Found integer: " << n << "\n"; 
} 

Pamiętaj, aby podać

#include <string> 
#include <sstream> 
+18

Masz na myśli parse: 'int n; while (strumień >> n) {std :: cout << "Znaleziono liczbę całkowitą:" << n << "\ n";} '? Znacznie czystszy –

1

Here is jak podzielić ciąg znaków na ciągi wzdłuż przestrzeni. Następnie możesz przetwarzać je jeden po drugim.

0

Spróbuj strtoken, aby najpierw oddzielić ciąg, a następnie zajmiesz się każdym ciągiem.

2
#include <string> 
#include <vector> 
#include <sstream> 
#include <iostream> 
using namespace std; 

int ReadNumbers(const string & s, vector <int> & v) { 
    istringstream is(s); 
    int n; 
    while(is >> n) { 
     v.push_back(n); 
    } 
    return v.size(); 
} 

int main() { 
    string s; 
    vector <int> v; 
    getline(cin, s); 
    ReadNumbers(s, v); 
    for (int i = 0; i < v.size(); i++) { 
     cout << "number is " << v[i] << endl; 
    } 
} 
8
#include <string> 
#include <vector> 
#include <iterator> 
#include <sstream> 
#include <iostream> 

int main() { 
    std::string input; 
    while (std::getline(std::cin, input)) 
    { 
     std::vector<int> inputs; 
     std::istringstream in(input); 
     std::copy(std::istream_iterator<int>(in), std::istream_iterator<int>(), 
     std::back_inserter(inputs)); 

     // Log process: 
     std::cout << "Read " << inputs.size() << " integers from string '" 
     << input << "'" << std::endl; 
     std::cout << "\tvalues: "; 
     std::copy(inputs.begin(), inputs.end(), 
     std::ostream_iterator<int>(std::cout, " ")); 
     std::cout << std::endl; 
    } 
} 
+3

+1 po zastąpieniu 1000 "std ::" s pojedynczym "using namespace std;" i wspomnieć o nagłówkach, których potrzebujesz. –

+5

+1, o ile zachowujesz prefiksy 'std ::'. Ich czytelność jest subiektywna (ja, będąc przyzwyczajony do nich, uważam, że jest o wiele lepiej czytać), ale zwiększona jasność poprzez w pełni kwalifikowane nazwy jest obiektywna. – sbi

+0

+1 po zastąpieniu przez "używanie przestrzeni nazw". Następnie możesz zamienić implementację na inną i nie musisz radzić sobie ze zmianą przestrzeni nazw. – nothrow

1
// get string 
std::string input_str; 
std::getline(std::cin, input_str); 

// convert to a stream 
std::stringstream in(input_str); 

// convert to vector of ints 
std::vector<int> ints; 
copy(std::istream_iterator<int, char>(in), std::istream_iterator<int, char>(), back_inserter(ints)); 
1

Generic rozwiązanie dla wartości bez znaku (zajmujących się przedrostkiem '-' bierze dodatkowy Bool):

template<typename InIter, typename OutIter> 
void ConvertNumbers(InIter begin, InIter end, OutIter out) 
{ 
    typename OutIter::value_type accum = 0; 
    for(; begin != end; ++begin) 
    { 
     typename InIter::value_type c = *begin; 
     if (c==' ') { 
      *out++ = accum; accum = 0; break; 
     } else if (c>='0' && c <='9') { 
      accum *= 10; accum += c-'0'; 
     } 
    } 
    *out++ = accum; 
     // Dealing with the last number is slightly complicated because it 
     // could be considered wrong for "1 2 " (produces 1 2 0) but that's similar 
     // to "1 2" which produces 1 0 2. For either case, determine if that worries 
     // you. If so: Add an extra bool for state, which is set by the first digit, 
     // reset by space, and tested before doing *out++=accum. 
} 
+0

+1, ponieważ odpowiadasz na pytanie w sposób podany ("liczby całkowite"), ale uważam, że jest to w pewnym sensie krok w kierunku mniej ogólnym, ponieważ wyraźnie działa tylko dla liczb całkowitych, a nie dla innych typów (IOW: "genericity" "nazwa_typu :: value_type accum = 0;" jest zawyżone). Jak poradzisz sobie na przykład z liczbami zmiennoprzecinkowymi? Jeśli chcesz napisać kolejny mini-lexer, nie zapomnij obsłużyć notacji naukowej i Inf, NaN itp. –

+0

Cóż, przyjmie on 'short []', 'std :: vector ' i 'std :: list 'lub w rzeczywistości każdy inny typ integralny (łącznie z typami definiowanymi przez implementację). Zwykły 0 zmieni się na wszystkie z nich. Powodem, dla którego użyłem tego rodzaju wartości, jest to, że nie będę przypadkowo przepełniał 'int', gdy użytkownik przechodzi jako' __int128 [] 'i ciąg znaków o dużych liczbach. – MSalters

+0

Używanie value_type dla accum jest absolutnie właściwą drogą, ale myślę, że nie rozumiem, dlaczego nie można po prostu użyć istringstream zamiast własnego ręcznego lexera. Wtedy mógłbyś pracować z każdym typem rozumianym przez operatora <<() (tj. Ulepszoną "typewizującą" ogólność) bez poświęcania "iteracyjnej" generyczności twojego obecnego rozwiązania. Podejrzewam też, że jest bardziej prawdopodobne, że będzie pracować z szerokimi znakami, lokalizacjami itp. –

18

C++ String Toolkit Library (Strtk) ma następujące rozwiązanie problemu:

#include <iostream> 
#include <string> 
#include <deque> 
#include <algorithm> 
#include <iterator> 

#include "strtk.hpp" 

int main() 
{ 
    std::string s = "1 23 456 7890"; 

    std::deque<int> int_list; 
    strtk::parse(s," ",int_list); 

    std::copy(int_list.begin(), 
      int_list.end(), 
      std::ostream_iterator<int>(std::cout,"\t")); 

    return 0; 
} 

Więcej przykładów można znaleźć: Here

Powiązane problemy