Reading numbers from textfile C++

Hey I've a textfile with letters and digits in it. I want to create a function that get integers from the textfile. I can do it for 9, 1, 3 etc but i fail to do it for 437, 98667 etc. How can i fix this? This is my code: (some parts are deleted and im dutch so it's in dutch :) ) It's about the 'void isGetal' part.

#include <iostream>
#include <fstream>
#include <string>
using namespace std;

void bestanden(ifstream & invoer, ofstream & uitvoer) { //Vraagt gegevens en controleert of deze juist zijn
string origineel; //Bestandsnaam invoer
string doelfile; //Bestandsnaam uitvoer
bestanden:
cout << "Voer de naam van het originele bestand in: " << endl;
cin >> origineel;
invoer.open(origineel, ios::in);
if (invoer.fail()) {
cout << "Kan het invoerbestand niet vinden." << endl;
goto bestanden;
} //if
cout << "Voer de gewenste naam van het doelbestand in: " << endl;
cin >> doelfile;
uitvoer.open(doelfile, ios::out);
}//bestanden

bool isCijfer(char c) { //Bepaalt of karakter een cijfer is
if (c > 48 && c < 58) { //Karakters 48 t/m 58 zijn cijfers in ASCII
return true;
}
else {
return false;
}
}//isCijfer

void isGetal(ifstream & invoer) {
char kar2=invoer.get();
int getal=0;
int j=1;
while (!invoer.eof()){
if (isCijfer(kar2)) {
getal = getal * 10 + kar2 - '0';
}
else {
if(getal>0) {
for(j=1; j < getal; j++) {
getal=0;
}
}
}
cout << getal << endl;
}
}

int main() {
ifstream invoer;
ofstream uitvoer;
char keuze; //keuze van de gebruiker (c of d)
vraag:
cout << "Goedendag, u kunt kiezen of u met dit programma een bestand wilt "
<< "coderen of decoderen." << endl;
cout << "Voor coderen voer een 'c' in. Voor decoderen voer een 'd'"
<< "in." << endl;
cin >> keuze;
bestanden(invoer, uitvoer);
isGetal(invoer);
return 0;
}//main
Are the numbers delimited (separated) by whitespace?

If so, let's say your file looks like this:

5378 4 14329
338 -42 1729 83
38 0 
3141529


This is as simple as a loop:

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <fstream>

int main()
{
    std::ifstream fin("file.txt");

    int number;
    while (fin >> number)
    {
        std::cout << number << '\n';
    }
}

5378
4
14329
338 
...


"while (fin >> number)" means "while (able to successfully extract another number)".
Last edited on
a textfile with letters and digits in it.


What is the format of the file? Can you provide an example. The best way of obtaining the numbers depends upon the format of the data that is used. Ganado's code above, for example, won't extract all the numbers if there is non-numeric data also present.
Yep I missed the "and letters" part.

Still, pieter, the >> operator is probably your friend here.
For example, if your text file looks like:
mystringA 01
mystringB 02
mystringC 03

then you can change your looping condition to be:
1
2
3
4
5
6
std::string str;
int num;
while (infile >> str >> num) // while successfully extracting both a string and then an int
{
    // ...
}
Last edited on
The file is a .txt file and contains letters and digits so for example:
jfdkj80nvnll9nvv93045

I want this:
80
9
93045

Oh and I don't want to use strings, is it possible to do this without?
Okay, if the text file is just a jumbled mess of digits and characters, then I suppose your approach makes more sense.

The core of your logic is your isCijfer and isGetal functions.
Your first issue is that in isCijfer, ASCII value 48 is '0', which is a valid digit, but your code says
1
2
if (c > 48 && c < 58)
    return true;


Your second issue is that in isGetal, you call invoer.get(); only once, but then never call it again when looping. So as far as I can tell, you have an infinite loop.

A questionable part of your code is:
1
2
3
for(j=1; j < getal; j++) {
    getal=0;
}

You're just assigning the same variable over and over again. The for loop is unnecessary.

___________________________

Suggestions:

1. isdigit is already a standard C/C++ library function, so you could replace isCijfer with that.
Be sure to #include <cctype>
2. Don't loop on .eof(). While it can work if you're careful or under certain conditions (like extracting one character at a time), it shouldn't be used as a looping condition in general. You should use the successful extraction of data as the looping condition.
3. Declare looping variables like i and j from within the for loops themselves. This makes it easier for a reader to easily tell what the lifetime of a variable is.
Last edited on
So, now I got this:
void isGetal(ifstream & invoer) {
    char kar = invoer.get();
    while(invoer >> kar) {
        if(isdigit(kar)) {
            cout << kar << endl;
            }
        kar = invoer.get();
    }
}



This is my input:
hhhh8 8
3 9 1
465
lkaskfdsf
frrs89


And this is what I get:
8
3
9
1
4
5
8


But I want:
8
8
3
9
1
465
89


What do I have to add?
1
2
3
4
5
6
char kar = invoer.get();
while(invoer >> kar)
{
    // ...
    kar = invoer.get();
}

Not exactly what I meant; you're skipping every other char by doing this, because you're extracting twice in a row each loop. But you're close.

Try making the while condition:
1
2
3
4
5
char kar;
while (invoer.get(kar)) // this says, "while successfully able to get the next char"
{
    // ...
}

and don't call kar = invoer.get();

____________________________

Once you fix that, you'll have a second issue, because you're only printing 1 digit every line.
There is more than one way to solve this, but one solution is to use a function called peek to see what the next character will be without actually extracting it.

http://www.cplusplus.com/reference/istream/istream/peek/

The other way to solve it is to use the digit = 10 * digit + kar - '0' strategy that you already employed.

Either way, you have to be careful about handling when a number ends.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void print_numbers2(std::ifstream& fin)
{
    char ch;
    while (fin.get(ch))
    {
        if (std::isdigit(ch))
        {
            std::cout << ch;

            char next_kar = fin.peek();
            while (std::isdigit(next_kar))
            {
                fin.get(next_kar);
                std::cout << next_kar;
                next_kar = fin.peek();
            }
            
            std::cout << '\n';
        }     
    }
}
Last edited on
It works!
A big, big thanks to you Ganado!
Am alternative using >> (simpler?) is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <sstream>
#include <cctype>

void print_nums(std::istream& is) {
	for (int ch, num; (ch = is.peek()) != EOF; )
		if (std::isdigit(ch)) {
			is >> num;
			std::cout << num << '\n';
		} else
			ch = is.get();
}


int main()
{
	const char* const data1 = "jfdkj80nvnll9nvv93045";

	const char* const data2 = "hhhh8 8\
		3 9 1\
		465\
		lkaskfdsf\
		frrs89";

	std::istringstream id1(data1);
	print_nums(id1);
	std::cout << "\n";

	std::istringstream id2(data2);
	print_nums(id2);
}

But what if I want to calculate something with all those numbers? So i used:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void print_numbers2(std::ifstream& fin)
{
    char ch;
    while (fin.get(ch))
    {
        if (std::isdigit(ch))
        {
            std::cout << ch;

            char next_kar = fin.peek();
            while (std::isdigit(next_kar))
            {
                fin.get(next_kar);
                std::cout << next_kar;
                next_kar = fin.peek();
            }
            
            std::cout << '\n';
        }     
    }
}


and now I want to multiplicate some numbers with 2 and some numbers with 3.
But which variable do I have to take to do this?
x=next_kar*2 doesn't work
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>
#include <sstream>
#include <string>
#include <algorithm>
#include <cctype>
#include <vector>
using namespace std;

vector<int> extract( istream &in )
{
   vector<int> result;
   for ( string line; getline( in, line ); ) 
   {
      replace_if( line.begin(), line.end(), []( char c ){ return !isdigit( c ); }, ' ' );
      stringstream ss( line );
      for ( int num; ss >> num; ) result.push_back( num );
   }
   return result;
}


int main()
{
   stringstream in( "hhhh8 8  \n"
                    "3 9 1    \n"
                    "465      \n"
                    "lkaskfdsf\n"
                    "frrs89   \n" );
   vector<int> V = extract( in );
   for ( int e : V ) cout << e << '\n';          // now do what you want with the numbers
}


8
8
3
9
1
465
89
Can it be done without strings?
Consider:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <sstream>
#include <cctype>
#include <vector>

std::vector<int> get_nums(std::istream& is) {
	std::vector<int> vi;

	for (int ch, num; (ch = is.peek()) != EOF; )
		if (std::isdigit(ch)) {
			is >> num;
			vi.push_back(num);
		} else
			is.ignore();

	return vi;
}

int main()
{
	const char* const data1 = "jfdkj80nvnll9nvv93045";
	const char* const data2 = "hhhh8 8\
		3 9 1\
		465\
		lkaskfdsf\
		frrs89";

	std::istringstream id1(data1);
	std::istringstream id2(data2);

	const auto v1 = get_nums(id1);
	const auto v2 = get_nums(id2);

	for (const auto& v : v1)
		std::cout << v << '\n';

	std::cout << "\n";

	for (const auto& v : v2)
		std::cout << v << '\n';
}


where get_nums() returns a vector of the extracted numbers.
Last edited on
Topic archived. No new replies allowed.