19

I need to read something like:

5 60 35 42
2 38 6
5 8
300 1500 900

And then save the first line in an array. After calling other functions do the same with the next line, and so on.

I try with gets() and then use sscanf() to scan the integers from the string, but I don't know how to read n numbers from a string.

R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Topo
  • 4,783
  • 9
  • 48
  • 70
  • See http://stackoverflow.com/questions/2619227/best-way-to-get-ints-from-a-string-with-whitespace or http://stackoverflow.com/questions/2195823/reading-unknown-number-of-integers-from-stdin-c – Steve Townsend Oct 17 '11 at 23:07
  • C++ would be best but it would be nice to know how in both. – Topo Oct 17 '11 at 23:08
  • 4
    @RobertoLapuente if you want to know the answer for C, it's best to ask another question (well, search first, I'm sure it has been answered before). *A good answer for each language will be completely different*. – R. Martinho Fernandes Oct 17 '11 at 23:57

7 Answers7

35

C++

If you have an unknown number of entries spread across an unknown number of lines, ending at EOF:

int n;
while(cin >> n)
  vector_of_int.push_back(n);

If you have a known number of entries spread across an unknown number of lines:

int n;
int number_of_entries = 20; // 20 for example, I don't know how many you have.
for(int i ; i < number_of_entries; ++i)
  if(cin >> n)
    vector_of_int.push_back(n);

If you have an uknown number of entries on a single line:

std::string str;
std::getline(std::cin, str);
std::istringstream sstr(str);
int n;
while(sstr >> n)
  vector_of_int.push_back(n);

If you have a unknown number of entries spread across a known number of lines:

for(int i = 0; i < number_of_lines; ++i) {
  std::string str;
  if(std::getline(std::cin, str)) {
    std::istringstream sstr(str);
    int n;
    while(sstr >> n)
      vector_of_int.push_back(n);
  }
}
  
Community
  • 1
  • 1
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • Is there a way to do this one line at a time? Because I think cin won't read the \n so I won't know when the line changes. – Topo Oct 17 '11 at 23:18
  • @RobertoLapuente - The first way that I have is correct for for the example you gave -- several numbers spread over several lines, ending at the end of the file. `cin` **will** read and ignore the `\n`, just as it reads and ignores the space characters. – Robᵩ Oct 17 '11 at 23:20
  • @Rob: I think each line is it's own array. He needs to run a function on each of the four arrays. – Mooing Duck Oct 17 '11 at 23:27
  • @Rob I can just do 'while (std::getline(std::cin, str)>0)' or something like that? what does 'getline' return value is when it reaches EOF? – Topo Oct 17 '11 at 23:32
  • 1
    @Roberto : For your purposes it returns `false` (not technically the case, but again, for _your_ purposes...), so `while (std::getline(std::cin, str))` is sufficient. – ildjarn Oct 17 '11 at 23:44
  • @MooingDuck - right, the last example is known number of lines, unknown number of entries. Or do I misunderstand your comment? – Robᵩ Oct 17 '11 at 23:57
  • @Rob: completely misread the "title" of the last one. My bad. – Mooing Duck Oct 18 '11 at 00:12
  • @MooingDuck - and I just realized that I missed the fundamental requirement of the original question (read data per line). Your answer beats mine. – Robᵩ Oct 18 '11 at 00:18
11

I've seen input files like this for competitions before. If speed is more of an issue than error detection, you could use a custom routine. Here's one similar to that I use:

void readintline(unsigned int* array, int* size) {
    char buffer[101];
    size=0;
    char* in=buffer;
    unsigned int* out=array;
    fgets(buffer, 100, stdin);
    do {
        *out=0;
        while(*in>='0') {
            *out= *out* 10 + *in-'0';
            ++in;
        }
        if (*in)
            ++in; //skip whitespace
        ++out;
    } while(*in);
    size = out-array;
}

It will destroy your memory if there's more than 100 characters on a line, or more numbers than array can hold, but you won't get a faster routine to read in lines of unsigned ints.

On the other hand, if you want simple:

int main() {
    std::string tmp;
    while(std::getline(std::cin, tmp)) {
        std::vector<int> nums;
        std::stringstream ss(tmp);
        int ti;
        while(ss >> ti) 
            nums.push_back(ti);
        //do stuff with nums
    }
    return 0;
}
Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • Actually the input is from an ACM programming competition. I have thought of something like this but I think there should be an easier way. – Topo Oct 17 '11 at 23:41
4

I'd probably write the code something like this:

// Warning: untested code.
std::vector<int> read_line_ints(std::istream &is) { 
    std::string temp;
    std::getline(is, temp);

    std::istringstream buffer(temp);
    int num;
    std::vector<int> ret;

    while (buffer>>num)
        ret.push_back(num);
    return ret;
}
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
3

In C++, you can use std::istringstream.

std::string nums = "1 20 300 4000";
std::istringstream stream(nums);
int a, b, c, d;
stream >> a >> b >> c >> d;
assert(a == 1 && b == 20 && c == 300 && d == 4000);

If you want to get it from the standard input, then do the same, but just use std::cin

std::cin >> a >> b >> c >> d;
Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
3

C++:

vector<int> ints;
while( !cin.eof() )
{
   int t;
   cin >> t;
   if ( !cin.eof() )
      ints.push_back(t);
}

Alternative (thx to Shahbaz)

int t;
vector<int> ints;
while(cin >> t)
   ints.push_back(t);
George Kastrinis
  • 4,924
  • 4
  • 29
  • 46
  • 2
    Because the `eof` flag doesn't get set until _after_ one attempts to read past the end, so the last element in `ints` will always be garbage data. – ildjarn Oct 17 '11 at 23:11
  • Please don't encourage the use of `eof()`. It doesn't do what people think, and it almost always results in a bug (like the one you have here). – Robᵩ Oct 17 '11 at 23:11
  • The use of eof (in general) has no problem. I fixed the code. – George Kastrinis Oct 17 '11 at 23:14
  • Is there a way to do this one line at a time? Because I think cin won't read the \n so I won't know when the line changes. – Topo Oct 17 '11 at 23:16
  • @George : No you didn't -- `while( !somestream.eof() )` is never correct, and in this case the last element of each `temp` instance will contain garbage data, as well as all the contents of the last element of `ints`. – ildjarn Oct 17 '11 at 23:22
  • If you don't believe that `eof()` is evil, consider [this program](http://ideone.com/MD3ZM). – Robᵩ Oct 18 '11 at 00:12
  • @Rob I can't replicate the problem. When I try it, it prints 3 numbers. – George Kastrinis Oct 18 '11 at 00:18
  • You placed an end-of-line before the end-of-file. Your loop gives different answers according to whether the final EOL is present or absent (in ideone it was absent). Additionally, your loop gives an infinite loop if there is any non-numeric data in the input. – Robᵩ Oct 18 '11 at 00:21
  • @Rob All common editors I have in mind, put the extra \n at the end of the file. Also in terminal you have to be in an empty new line to give the EOF signal. Additionally the op didn't ask for error handling. – George Kastrinis Oct 18 '11 at 00:23
3

The quick solution is to read them with scanf()

int array[1000];
int index = 0;

while ((index < 1000) && (scanf("%d", &tmp) == 1)) {
    array[index++] = tmp;
}

This still needs a bit more validation ...

pmg
  • 106,608
  • 13
  • 126
  • 198
  • 3
    Don't forget the space before `%d` to eat any whitespace. – Tom Zych Oct 17 '11 at 23:10
  • @RobertoLapuente I would go with this solution too. Many people don't know the usage of return value of `scanf`, so even if you decided to go with c++'s `istringstream`, make sure you learn this point. – Shahbaz Oct 17 '11 at 23:11
  • @TomZych It is automatically done. The problem with space before `%d` is that if before the first integer there is no space, then `scanf` fails (because you told it that it **must** see whitespace) – Shahbaz Oct 17 '11 at 23:11
  • @Shahbaz: Good point. I don't use `scanf`, too fragile, I was remembering something from my C course back in '91. I guess you put the space after instead? I'm pretty sure you need it. – Tom Zych Oct 17 '11 at 23:17
  • @TomZych, then you would force `scanf` to read space after the last number which may not be existent. In general `scanf` ignores whitespace before and after your format string **unless** you are requesting `%c` which reads space as a character also and doesn't ignore it. In that case, yes you need space. – Shahbaz Oct 17 '11 at 23:18
  • Is there a way to do this one line at a time? Because I think cin won't read the \n so I won't know when the line changes. – Topo Oct 17 '11 at 23:19
  • I answered with C in mind. I "have no idea" what `cin` is :-) – pmg Oct 17 '11 at 23:40
  • @pmg: `cin` is the "C++ version" of `stdin`. Basically, your code ignores the newline characters. – Mooing Duck Oct 17 '11 at 23:49
  • @Shanbaz: White space (such as blanks, tabs, or newlines) in the format string match any amount of white space, including none, in the input.. – Martin York Oct 17 '11 at 23:58
0

In C++ it's extremely simple to read N integers separated by whitespace via stdin:

#include <iostream>

using namespace std;

const unsigned N = 5;

int main(void)
{
   int nums[N];

   for (unsigned i = 0; i < N; ++i)
      cin >> nums[i];

   cout << "Your numbers were:\n";

   for (unsigned i = 0; i < N; ++i)
      cout << nums[i] << " ";

   cout << "\n";

   return 0;
}
AusCBloke
  • 18,014
  • 6
  • 40
  • 44
  • I took to long to post because I couldn't see the code button. :/ Also I used an array rather than a vector since we already know the size of the array, hope Stroustrup won't mind. Note also, no error handling in this code. – AusCBloke Oct 17 '11 at 23:12