I'd like to make a function reading integers from stdin in the fastest way possible. To be more specific I've got a very long sequence of ints separated with spaces finished with EOF on which I've got to make some operations. I used cin but since this function is quite robust in its possibilites I'd like to make something smaller, more specific, and thus, faster and more efficient. I've read that the fastes way of reading stdin is getChar() function. I've already found some implementations of this problem in C, but chars there are just another form of integers and in C++ they're not. My idea is to make a function of type bool
bool myIntRead( int *num);
which would basically read chars of an integer until a 'space sign' and somehow put these chars back into an int number. The bool type would be used to inform that the stdin is empty.
int main() {
int num;
while (myIntRead(&num) ) {
myIntRead(&num)
//some operations on num
}
}
I'm fully aware that this sort of dealing with input is much more complex and more difficult but I'd really like to find out a solution to this problem. I will really appreaciate some help from You Guys in finding out an implementation of this concept.
Best regards!
If you only want to deal with ASCII input and you're sure that input will be provided to you in the expected format (ie. digits 0-9 followed b space) then all you have to do is read each character and then:
Determine if it's a space or a digit
If it's a space, start reading a new number
If it's a digit append digit to current number
The following illustrates that. But it doesn't handle any overflow and there is no stop condition..
int c;
int current_number;
std::vector<int> numbers;
do {
c = getchar();
if(c == 0x20) // it's a space
{
numbers.push_back(current_number); // add the current number to the list of numbers
current_number = 0; // reset the current_number variable back to 0
}else if(c >= 0x30 && c <= 0x39) // it's a digit
{
current_number = current_number * 10 + (c - 0x30); // add next digit..
// you may want to deal with overflow here (ie. if number of digits > MAX_DIGITS or similar)
}
} while (TRUE); // there should be a stop condition here..
the following code reads 1000000 (one million) numbers in 61ms (normal PC some years old). The file has the size of 3.8MB.
#include <iostream>
#include <fstream>
#include <iterator>
#include <vector>
template< typename T, typename Out >
struct IntReader_type
{
IntReader_type( Out out ) : out_( out ) {}
template< typename E, typename Traits >
friend std::basic_istream< E, Traits >& operator>>( std::basic_istream< E, Traits >& in, IntReader_type rdr )
{
std::basic_istream< E, Traits >::sentry ok( in );
if( ok )
{
std::ios_base::iostate state = std::ios_base::goodbit;
try
{
const std::ctype< E >& ct = std::use_facet< std::ctype< E > >( in.getloc() );
while( state == std::ios_base::goodbit )
{
T result = T(0);
for( Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() )
{
if( Traits::eq_int_type( m, Traits::eof() ) )
{
state |= std::ios_base::eofbit; // EOF is not an error
break;
}
const E c = Traits::to_char_type( m );
if( ct.is( std::ctype_base::space, c ) )
break;
if( ct.is( std::ctype_base::digit, c ) )
{
(result *= 10) += T(c - E('0'));
}
else
{
state |= std::ios_base::failbit; // not a digit
break;
}
}
*(rdr.out_)++ = result; // store the number
// skip white space character
for( Traits::int_type m = in.rdbuf()->sgetc(); ; m = in.rdbuf()->snextc() )
{
if( Traits::eq_int_type( m, Traits::eof() ) )
{
state |= std::ios_base::eofbit;
break;
}
if( !ct.is( std::ctype_base::space, Traits::to_char_type( m ) ) )
break;
}
}
}
catch( ... )
{
state |= std::ios_base::badbit;
if( in.exceptions() & std::ios_base::badbit )
throw;
}
in.setstate( state );
}
return in;
}
private:
Out out_;
};
template< typename T, typename Out >
IntReader_type< T, Out > read_ints( Out out )
{
return IntReader_type< T, Out >( out );
}
call the Integer-reader in this way:
vector< int > numbers;
if( cin >> read_ints< int >( back_inserter(numbers) ) )
{ // read is done without error
be careful - in this version only unsigned numbers can be read and there is no check of integer overflow.
ok, so c = getchar();
You have to store in in an array for it to be processable.
If c contains one digit, multiply c[0] * 1.
If c contains two digits, multiply c[1] * 1 + c[0] * 10.
If c contains three digits, multiply c[2] * 1 + c[1] * 10 + c[0] * 100.
If c contains four digits, multiply c[3] * 1 + c[2] * 10 + c[1] * 100 + c[0] * 1000, etc etc.
The code for input looks like this:
while(digitInput!=13)
{
if (kbhit())
{
digitInput=getch();
if (digitInput==27) exit(0);
if ((digitInput>47) && (digitInput<59))
{
digitArray[digit]=(unsigned char)digitInput-48;
digit++;
printf("%d",digitInput-48);
}
if (digitInput==13) { digitn=digitArray[0]; break; }
}
}
switch(digit)
{
case 0:
case 1:
digitn=digitArray[0]*1 ;
break;
case 2:
digitn= digitArray[1]*1 +digitArray[0]*10 ;
break;
case 3:
digitn= digitArray[2]*1+digitArray[1]*10 +digitArray[0]*100 ;
break;
case 4:
digitn=digitArray[3]*1+digitArray[2]*10+digitArray[1]*100+digitArray[0]*1000 ;
break;
}
The complete app in code.
#include <iostream.h>
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
int digit=0,digitInput=0;
int digitArray[10]={0},digitn;
int numberOfInputDigits=4;
/*********************************
* *
********************************/
void getIntKey(void)
{
digitArray[0]=0;
digitArray[1]=0;
digit=0;
digitInput=0;
while(digitInput!=13)
{
if (kbhit())
{
digitInput=getch();
if (digitInput==27) exit(0);
if ((digitInput>47) && (digitInput<59))
{
digitArray[digit]=(unsigned char)digitInput-48;
digit++;
printf("%d",digitInput-48);
}
if (digitInput==13) { digitn=digitArray[0]; break; }
}
}
switch(digit)
{
case 0:
case 1:
digitn=digitArray[0]*1 ;
break;
case 2:
digitn= digitArray[1]*1 +digitArray[0]*10 ;
break;
case 3:
digitn= digitArray[2]*1+digitArray[1]*10 +digitArray[0]*100 ;
break;
case 4:
digitn=digitArray[3]*1+digitArray[2]*10+digitArray[1]*100+digitArray[0]*1000 ;
break;
case 5:
digitn=digitArray[4]*1+digitArray[3]*10+digitArray[2]*100+digitArray[1]*1000+digitArray[0]*10000 ;
break;
case 6:
digitn=digitArray[5]*1+digitArray[4]*10+digitArray[3]*100+digitArray[2]*1000+digitArray[1]*10000
+digitArray[0]*100000;
break;
case 7:
digitn=digitArray[6]*1+digitArray[5]*10+digitArray[4]*100+digitArray[3]*1000+digitArray[2]*10000
+digitArray[1]*100000 +digitArray[0]*1000000;
break;
case 8:
digitn=digitArray[7]*1+digitArray[6]*10+digitArray[5]*100+digitArray[4]*1000+digitArray[3]*10000
+digitArray[2]*100000 +digitArray[1]*1000000+digitArray[0]*10000000;
break;
case 9:
digitn=digitArray[8]*1+digitArray[7]*10+digitArray[6]*100+digitArray[5]*1000+digitArray[4]*10000
+digitArray[3]*100000 +digitArray[2]*1000000+digitArray[1]*10000000 +digitArray[0]*100000000;
break;
}
// if (digitInput!=13) digitn=digitArray[3]*1+digitArray[2]*10+digitArray[1]*100+digitArray[0]*1000 ;
printf("\n%i\n\n",digitn);
}
/*********************************
* *
********************************/
int main()
{
system("color 1F"); //Blue background
printf("Digits Into decimal numbers \n ");
printf("Max Input is %d Digits \n ",numberOfInputDigits);
printf("\nInput Digit >");
getIntKey();
printf("\nThe input was digitArray[7]=%d \n",digitArray[7]);
printf("digitArray[6]=%d \n",digitArray[6]);
printf("digitArray[5]=%d \n",digitArray[5]);
printf("digitArray[4]=%d \n",digitArray[4]);
printf("digitArray[3]=%d \n",digitArray[3]);
printf("digitArray[2]=%d \n",digitArray[2]);
printf("digitArray[1]=%d \n",digitArray[1]);
printf("digitArray[0]=%d \n",digitArray[0]);
printf("\n%i\n\n",digitn);
return 0;
}
Related
I'm new in C programing language . I have a question how can I end loop in windows.
#include<stdio.h>
#include<conio.h>
int main() {
printf("enter ur palindrome");
int i[100], c, d = 0, n = 0;
c = getchar();
while (c != '\n') {
i[d] = c;
d++;
}
loop:
while (d != 0) {
if ((i[0 + n] != i[d - n])) {
n++;
goto loop;
}
printf("this is not a palindrome");
break;
}
printf("this is a palindrome");
return (0);
}
I HAVE TRIED ALMOST EVERYTHING CTRL+Z, CTRL+C, CTRL+D, REPLACING '\n' WITH EOF
and many more thing. Nothing worked for me. I'm using CodeBlocks on Windows 10.
Is there some other way of writing such type of program other then getchar and eof.
Rather than using goto, use continue; to repeat the loop.
However, there are a number of other problems with the posted code.
The array `int i[100]` is never terminated with a NUL byte ('\0')
An array of char not int should be used.
this loop: `while (d != 0) will never exit,
because (if the loop is ever entered)
the `d` variable is never changed within the loop
Here is my suggested code:
caveat: not thoroughly tested
#include <stdio.h>
//#include<conio.h> <-- not used, so do not include
#include <string.h>
#define MAX_LENGTH (100)
int main( void )
{
int d;
int n;
char i[MAX_LENGTH];
printf("enter ur palindrome\n"); // <-- \n so will immediately print
if ( NULL != fgets( i, MAX_LENGTH, stdin ) )
{
if( strlen( "\n" ) < strlen( i ) )
{ // then, some string entered
// remove any trailing '\n'
char *newline = strstr( i, "\n" );
if( newline )
{ // then '\n' found
*newline = '\0';
} // end if
d = strlen( i );
for( n=0; (d-n) >= n; n++ )
{
if( i[0 + n] != i[d - n] )
{ // then no match
printf("this is not a palindrome");
break;
} // end if
} // end for
if( (d-n) < n )
{ // then palindrome
printf("this is a palindrome");
} // end if
}
else
{
printf( "nothing entered\n");
} // end if
} // end if
return (0);
} // end function: main
you probably want to take a look at this section again
c=getchar();
while(c!= '\n') // Value of c never altered, hence it'll never break
{ i[d]=c;
d++;
}
and yes, the other loop
loop: // not required
while ( d!=0 ) // can't see change in d within this body, is it n ?
{
if((i[0+n] != i[d-n]))
{
n++;
goto loop; // not required
continue; //will do
}
printf("this is not a palindrome");
break;
}
and you'd actually get an extra message saying
this is a palindrome
after printing
this is a not palindrome
which I suppose is not what you want.
In that loop you need to again read next character so need to add getchar() in that loop
c=getchar();
while(c!= '\n')
{
i[d]=c;
d++;
c=getchar();
}
Here's a different way to write that piece of code
#include <stdio.h>
#include <string.h>
int main()
{
char *word;
int i;
int n;
printf( "enter ur palindrome" );
scanf("%[^\n]", word); // reads input all the way to the new line
n = strlen( word );
for ( i = 0; i < n; i++ ) {
if ( word[ i ] != word[ n-1 - i ] ) {
break; /* for */
}
}
if ( i == n ) {
printf( "This is a palindrome");
} else {
printf( "This is not a palindrome" );
}
return 0;
}
How could I use arithmetic operation symbols if its assigned to variable and need to return for evaluation of simple arithmetic problems --> from the way its constructed in the code below. How? or any other suggestions welcomed thanks in advance
int arithmeticType();
int main() {
int arithmeticSymbol = arithmeticType();
int x, y;
int result = 0;
srand(time(NULL) );
printf( "Type is: %d\n", arithmeticSymbol );
//how to get result of (x + y)using arithmeticSymbol?????
printf( "The result %d %d %d", 4, arithmeticSymbol, 3 );
return 0;
}
int arithmeticType() {
int type, token;
printf("Select the type of arithmetic operation to perform:\n"
"\t1. Addition.\n\t2. Subtraction.\n\t3. Multiplication.\n\t"
"4. Mixture of all three. --> ");
scanf("%d", &type);
switch ( type ) {
case 1:
token = '+';
break;
case 2:
token = '-';
break;
case 3:
token = '*';
break;
case 4:
//get random value between 1-3
token = rand() % 3 + 1;
if ( token == 1 ) {
token = '+';
}
else if ( token == 2 ) {
token = '-';
}
else {
token = '*';
}
break;
default:
printf("Wrong input");
break;
}
return token;
}
Write a function to apply the operator. You also need to consider your types - if you just use integers, things like 3/2 may not do what you expect...
If it was me I'd probably use an enum instead of the ASCII operator code.
int getResult(int op1, int op, int op2)
{
int result = 0;
if (op== '+') {
result = op1 + op2;
}
else ...
return result;
}
You would call it like: printf( "The result of %d %c %d is: %d", 4, getOperation, 3, getResult(4, getOperation, 3) ); (note printing the operator as %d wont do what you expect and the name getOperator is very misleading.
i have this line taken from a txt file (first line in the file):
#operation=1(create circle and add to picture) name X Y radius.
why does this code doesnt take the integer 1 and puts it into k?
Circle Circle::CreateCirc(Circle c){
int k;
ifstream myfile("cmd.txt");
if (!myfile.is_open())
cout<<"Unable to open the requested file "<<endl;
string line,line2="create circle";
for (int i=1;i<countrows();i++)
{
getline(myfile,line);
if (line.find(line2)!=string::npos)
{
istringstream ss(line);
ss>>k;
cout<<k<<endl;
}
}
return c;
}
instead im getting adress memory...help plz
Because the line doesn't start with a number. You'll need to skip over the #operation= part before extracting a number.
You should check the result of the extraction, and of getline, to help identify what's going wrong when these fail.
Also, if countrows() returns the expected number of rows in the file, then your loop would miss out the last one. Either loop from zero, or while i <= countrows(); or, if you want to process every line in the file, you could simply loop while (getline(myfile,line)).
If the actual text in the file you try to read starts with "#operation=1" and you want the number 1 from that, you can't use the simple input operator. It will read the character '#' first, which isn't a digit and so the parsing will fail and k will not be initialized. And if k is not initialized, it will be of indeterminate value, and reading that value will lead to undefined behavior and seemingly random output.
You need to check that the extraction worked:
if (ss >> k)
std::cout << k << '\n';
That won't solve your problem though, as like I said above, you can't use the simple input operator here. You need to parse the string using other methods. One way might be to find the equal character '=' and get a sub-string after that to try and extract the number.
try this:
Circle Circle::CreateCirc(Circle c){
const std::streamsize ALL = std::numeric_limits< std::streamsize >::max(); // #include <limits> needed
int k;
ifstream myfile("cmd.txt");
if (!myfile.is_open())
cout<<"Unable to open the requested file "<<endl;
for (int i=1;i<countrows(); ++i, myfile.ignore(ALL,'\n') ) // skip rest of the line
{
if( myfile.ignore(ALL,'=') >> k )
{
cout<<k<<endl;
}
else
break; // read error
}
return c;
}
EDIT: A way to do it not much bit a little closer to the way you were trying to do it using atoi() rather than streams.
#include <iostream>
#include <cstdlib> // for atoi()
int main(){
std::string str = "#operation=1(create circle and add to picture) name X Y radius.";
int k;
std::string line=str, line2="(create circle";
std::size_t fnd = line.find(line2);
if (fnd!=std::string::npos)
{
k = atoi(&str[fnd-1]); // int atoi(const char *str) == argument to integer
std::cout<< k << " " << str[fnd-1] << str[fnd] << " ";
}
}
There are a few ways to extract an integer from a string but i like to filter out the digit from the string;
#include <iostream>
int main(){
std::string str = "#operation=1(create circle and add to picture) name X Y radius.";
int k = 0;
// an array of our base10 digits to filter through and compare
const char digit[] = {'0','1','2','3','4','5','6','7','8','9'};
for(int s_filter = 0; s_filter<str.size(); ++s_filter){
for(int d_filter = 0; d_filter<10; ++d_filter){
// filter through each char in string and
// also filter through each digit before the next char
if(digit[d_filter] == str[s_filter]) {
// if so the char is equal to one of our digits
k = d_filter;// and d_filter is equal to our digit
break;
} else continue;
}
}
switch(k) {
case 1:
std::cout<< "k == 1";
// do stuff for operation 1..
return 0;
case 2:
std::cout<< "k != 1";
// do more stuff
break;
//case 3: ..etc.. etc..
default:
std::cout<< "not a digit";
return 1;
}
}
// find_num.cpp (cX) 2015 adolfo.dimare#gmail.com
// http://stackoverflow.com/questions/21115457/
#include <string> // std::string
#include <cctype> // isnum
/// Find the number in 'str' starting at position 'pos'.
/// Returns the position of the first digit of the number.
/// Returns std::string::npos when no further numbers appear within 'str'.
/// Returns std::string::npos when 'pos >= str.length()'.
size_t find_num( const std::string str, size_t pos ) {
size_t len = str.length();
bool isNegative = false;
while ( pos < len ) {
if ( isdigit(str[pos]) ) {
return ( isNegative ? pos-1 : pos );
}
else if ( str[pos]=='-' ) {
isNegative = true;
}
else {
isNegative = false;
}
++pos;
}
return std::string::npos;
}
#include <cassert> // assert()
#include <cstring> // strlen();
int main() {
std::string str;
str = "";
assert( std::string::npos == find_num( str, 0 ) );
assert( std::string::npos == find_num( str, 9 ) );
str = "#operation=1(create circle and add to picture) name X Y radius.";
assert( strlen("#operation=") == find_num( str, 0 ) );
str = "abcd 111 xyx 12.33 alpha 345.12e-23";
/// 0123456789.123456789.123456789.123456789.
assert( 5 == find_num( str, 0 ) );
assert( 13 == find_num( str, 5+3 ) );
assert( 25 == find_num( str, 20 ) );
str = "abcd-111 xyx-12.33 alpha-345.12e-23";
/// 0123456789.123456789.123456789.123456789.
assert( 4 == find_num( str, 0 ) );
assert( 12 == find_num( str, 5+3 ) );
assert( 24 == find_num( str, 20 ) );
str = "-1";
assert( 0 == find_num( str, 0 ) );
assert( 1 == find_num( str, 1 ) );
assert( std::string::npos == find_num( str, 2 ) );
assert( std::string::npos == find_num( str, strlen("-1") ) );
return 0;
}
I have to read in a csv file with 5 fields (int , char[], char[], char[], float) that looks like that :
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
I have to put the fields in a struct, and then put the struct after one line is complete, into a array of the struct type ...
for the learning effect, we are only allowed to use LOW-LEVEL coding, and only use functions like fgetc, strcpy and no strings, only char[]...
Now I made my algorithm to read the textfile character by character, but I have problems separating them correctly, putting them together again and assigning them to the struct fields correctly. Here is my Code:
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
using namespace std;
int main(int argc, char **argv)
{
struct Stud{
long matrnr;
char vorname[30];
char name[30];
char datum[30];
float note;
};
const int MAX = 30;
Stud stud;
Stud mystud[30]; // <<-- Array of "Stud" type
//memset((void*)mystud,0,sizeof(mystud) * sizeof(Stud));
int wordCounter(0);
int i(0); //thats the charCounter or index
int studentCounter(0);
char wort[MAX];
//int matrnr;
//char vorname[MAX];
//char name[MAX];
//char datum[MAX];
//float note;
FILE * pFile;
int cnr(0);
pFile=fopen("studentendaten.txt","r");
if (pFile==nullptr)
{
perror ("Fehler beim öffnen der Datei");
}
else
{
while (cnr != EOF)
{
(cnr=fgetc(pFile)) ;
if ((char)cnr == '\n') {
mystud[studentCounter] = stud;
studentCounter++;
continue;
}
if ((char)cnr == ';') {
wort[i] = '\0';
switch (wordCounter % 5) {
case 0:
stud.matrnr = atol(wort);
break;
case 1:
strcpy(stud.name, wort);
break;
case 2:
strcpy(stud.vorname, wort);
break;
case 3:
strcpy(stud.datum,wort);
break;
case 4:
stud.note = atof(wort);
break;
}
wordCounter++;
i = 0;
continue;
}
if (wordCounter % 5 == 0 && (char)cnr != ';') {
wort[i] = (char)cnr;
i++;
//stud.matrnr = atol(wort);
}
if (wordCounter % 5 == 1) {
wort[i] = (char)cnr;
i++;
//strcpy(stud.name, wort);
}
if (wordCounter % 5 == 2) {
wort[i] = (char)cnr;
i++;
//strcpy(stud.vorname, wort);
}
if (wordCounter % 5 == 3) {
wort[i] = (char)cnr;
i++;
//strcpy(stud.datum,wort);
}
if (wordCounter % 5 == 4) {
wort[i] = (char)cnr;
i++;
//stud.note = atof(wort);
}
}
fclose (pFile);
}
for (int i(0) ; i <= studentCounter; i++) {
cout <<mystud[i].matrnr << " " << mystud[i].name << " " << mystud[i].vorname <<" "
<< mystud[i].datum <<" " << mystud[i].note << endl;
//printf("%5ld %5s %5s %5s %5f \n",mystud[i].matrnr,mystud[i].name,mystud[i].vorname,mystud[i].datum,mystud[i].note);
}
return 0;
}
I am not sure if it has to do with a wrong increment variables, or the fact that I don't put an '\0' at the end of my wort[] array..and therefore not recognizing the end of my array? And if so, how do I do it without knowing where the end exactly is... ? (I don't know the length of the words..)
EDIT: I updated my code again, the only thing that wonders me is that the LAST LINE IS NOT BEING CORRECTLY PARSED , its showing some rubbish, and I can't see the error in my code...
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
2345678;Meier;Hans;12.10.1985;2.4;
1234567;Müller;Fritz;17.05.1990;1.9;
8392019;Thomas;Kretschmer;28.3.1920;2.5;
3471144;Mensch;Arbeit;29.2.2013;4.5;
2039482;Test;Test;30.20.2031;2.0;
7584932;Bau;Maschine;02.02.2010;2.3;
Suggestion: use a case structure for the parsing, and make yourself a "copyToSemicolon" function: then you can write things like
sIndexCount = 0;
char temp[50];
while((cnr=fgetc(pFile)) != EOF) {
offset = 0;
for(var = 0; var < 5; var++ {
switch(var) {
case 0:
offset = copyToSemicolon(temp, cnr, offset) + 1;
stud.matrnr = atoi(temp);
break;
case 1:
offset = copyToSemicolon(mystud[sIndexCount].vorname, cnr, offset) + 1;
break;
... etc
}
}
sIndexCount++;
if(sIndexCount == 50) break; // in case the input file is longer than our structure
}
And you need a function copyToSemicolon that takes two char* pointers as inputs, and that copies characters from the second string (starting at offset) until it reaches either a semicolon or the end of line - and that returns the offset it reached (last character read).
int copyToSemicolon(char* dest, char* source, int offset) {
while(source[offset] != ';' && source[offset] != '\n') {
*dest = source[offset++];
dest++;
}
return offset;
}
EDIT strtok method:
sIndexCount = 0;
char temp[50];
while((cnr=fgetc(pFile)) != EOF) {
offset = 0;
temp = strtok(cnr, ';');
for(var = 0; var < 5; var++ {
switch(var) {
case 0:
stud.matrnr = atoi(temp);
break;
case 1:
strcpy(mystud[sIndexCount].vorname, strtok(NULL, ';'));
break;
... etc
case 4:
mystud[sIndexCount].note = atof(strtok(NULL, '\n'));
}
}
sIndexCount++;
if(sIndexCount == 50) break; // in case the input file is longer than our structure
}
One issue that I am seeing is that your code copies or parses one character at a time, such that when you're reading 2345678;Meier;Hans;12.10.1985;2.4; you first set stud.matrnr to 2, then 23, then 234, then 2345, then 23456, then 234567, then 2345678. Similarly, for stud.name, you first set it to M, then the Me, then to Mei, etc. I propose to you to think of things in a different way. I'll give you some pseudocode:
while (!eof) {
get character from file
if (character isn't ';' and isn't '\n') {
copy character into buffer (increment buffer index)
} else if (character is ';') {
it's the end of a word. Put it in its place - turn it to an int, copy it, whatever
reset the buffer
} else if (character is '\n') {
it's the end of the last word, and the end of the line. Handle the last word
reset the buffer
copy the structure
}
}
This should make life a lot easier on you. You're not changing your data nearly as much, and if you need to debug, you can focus on each part on its own.
Generally, in programming, the first step is making sure you can say in your native speaking language what you want to do, then it's easier to translate it to code. You're close with you implementation, and you can make it work. Just be sure you can explain what should be happening when you see ';' or '\n'.
Since you have tagged this as C++, you should consider using std::getline for reading the line from the file, the use std::getline(file, text_before_semicolon, ';') for parsing the fields.
You could also use std::istringstream for converting the textual representation in the text line to internal numeric format.
This is taken right from The "C++ Programming Language" by Bjarne Stroustrup. I would just like some clarification on how he accumulating the digits into the variable (int number_value). Please don't rip on the code, I didn't write it (Complete code from chapter 6 at bottom of post).
Specifically as the parser is calling the lexer, how is the lexer building up a number by using cin. I believe the answer is in these eight lines, but I would like an explanation of how it works.
if( isalpha( ch ) ) {
(*input).putback( ch );
(*input) >> string_value;
return curr_tok=NAME;
} else {
error( "bad token " );
return curr_tok=PRINT;
}
It appears to me that the first time get_token( ) is called, it puts the full expression_list into cin or whatever input stream input points to (inside get_token( )).
(*input) >> ch;
I know ch is declared as a char, but what happens if you type 123.4+5.432; (assuming input is cin) cin now contains the "string" 123.4+5.432 contained in its stream. Then we move to the switch statement in the lexer (get_token( )). I am assuming: :
ch == 1?
at this point? Next inside the switch statement, we would "fall through" to the '.' case. Here we place '1' back into the stream and write it out to number_value?
(*input).putback( ch );
(*input) >> number_value;
Now number_value = 1, and we return to the parser. Since we found a NUMBER it calls get_token( ) again. And cin operator<< is called again. Wouldn't the next call to (*input)>> number_value place 2 into number value overwriting the 1 (assuming that the input is still 123.4+5.432)? What happens here. I guess I need a better understanding of how streams work. If someone could take the time, and give a brief explanation and point me to a good resource I would greatly appreciated.
Thank you,
Matthew Hoggan
For those that don't have the book, the code is:
#include <iostream>
#include <stdlib.h>
#include <string>
#include <sstream>
#include <map>
#include <cctype>
std::istream *input;
double number_value;
int no_of_errors;
std::string string_value;
std::map<std::string,double> table;
enum Token_value {
NAME, NUMBER, END,
PLUS='+', MINUS='-', MUL='*', DIV='/',
PRINT=';', ASSIGN='=', LP='(', RP=')'
};
Token_value curr_tok=PRINT;
double expr( bool );
double term( bool );
double prim( bool );
Token_value get_token( );
double error( std::string s ) {
no_of_errors++;
std::cerr << "error: " << s << std::endl;
return 1.0;
}
Token_value get_token( ) {
char ch = 0;
(*input) >> ch;
switch( ch ) {
case 0: {
return curr_tok=END;
}
case ';':
case '*':
case '/':
case '+':
case '-':
case '(':
case ')':
case '=': {
return curr_tok = static_cast<Token_value>( ch );
}
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '.': {
(*input).putback( ch );
(*input) >> number_value;
return curr_tok=NUMBER;
}
default: {
if( isalpha( ch ) ) {
(*input).putback( ch );
(*input) >> string_value;
return curr_tok=NAME;
} else {
error( "bad token " );
return curr_tok=PRINT;
}
}
}
}
int main( int argc, char *argv[ ] ) {
switch( argc ) {
case 1: {
input = &std::cin;
break;
}
case 2: {
input = new std::istringstream( argv[1] );
break;
}
default: {
error(" To many arguments" );
return 1;
}
}
table["pi"] = 3.1415926535897932385;
table["e"] = 2.7182818284590452354;
while( (*input) ) {
get_token( );
if( curr_tok == END ) {
break;
}
if( curr_tok == PRINT ) {
continue;
}
std::cout << expr( false ) << std::endl;
}
if( input != &std::cin ) {
delete input;
}
return 0;
}
double expr( bool get ) {
double left = term( get );
for( ; ; ) {
switch( curr_tok ) {
case PLUS: {
left += term( true );
break;
}
case MINUS: {
left -= term( true );
break;
}
default: {
return left;
}
}
}
}
double term( bool get ) {
double left = prim( get );
for( ; ; ) {
switch( curr_tok ) {
case MUL: {
left *= prim( true );
break;
}
case DIV: {
if( double d = prim( true ) ) {
left /= d;
break;
}
else {
return error( "divide by 0" );
}
}
default: {
return left;
}
}
}
}
double prim( bool get ) {
if( get ) {
get_token( );
}
switch( curr_tok ) {
case NUMBER: {
double v = number_value;
get_token( );
return v;
}
case NAME: {
double &v = table[string_value];
if( get_token( ) == ASSIGN ) {
v = expr( true );
return v;
}
}
case MINUS: {
return -prim( true );
}
case LP: {
double e = expr( true );
if( curr_tok != RP ) {
return error( "')' expected" );
}
get_token( );
return e;
}
default: {
return error( "primary expected" );
}
}
}
The 'trick' is caused by the differing behaviour of the following three lines:
char ch; std::cin >> ch;
std::string string_value; std::cin >> string_value;
double number_value; std::cin >> number_value;
The first just gets a single character, the second and third get multiple characters to build a variable of the correct type.
Strings overload the global operator>> function to provide a version for strings, and this version uses whitespace as the delimiter (if you need to input spaces with your string, you should look into getline).
The double version uses the istream& operator>> (double& val); member function and will read characters only while they make sense in forming a double value.
So, let's say you enter abc. The code cin >> ch will populate ch with the character 'a', removing it from the input stream. You will then detect this with isapha in the default case since it matches none of the other cases.
At that point, you push that character 'a' back on to the input stream so you can re-read it, and execute cin >> string_value which gets the entire string abc, not a single character.
Similarly, if you entered 3.14159, it would be caught by the case '3' check, the character would be pushed back on to the input stream, and cin >> number_value would then get the whole value.
Now number_value = 1, and we return to the parser.
No. (*input) >> number_value; reads-in the whole double, i.e., 123.4 since number_value is of type double. Other than this, you are right.