# Weight Change Program - Help

This is a discussion on Weight Change Program - Help within the C Programming forums, part of the General Programming Boards category; The program is given input from a file in the following format: A 45kg 52lb 5 B 82lb 15st 8 ...

1. ## Weight Change Program - Help

The program is given input from a file in the following format:

A 45kg 52lb 5
B 82lb 15st 8
C 14st 80kg 10
etc..

Where A is a user identifier, 45kg is the starting weight, 52lb is the ending weight, and 5 is the numbers of days that has elapsed. (In the second line st stands for stones and is equal to 14lb).

The program must compute and output the average weight gain/loss per day in kg for each line of input. There's also a whole bunch of validation for each input but I've pretty much covered those. I know how to get the digits from each line of input and I know how to convert ASCII to int. What I don't know is how to separate the numbers from each other so I can do the conversions necessary to calculate the weight change.

What I mean by this, taking line 1 as an example:

How do I separate 45 from 52 from 5 and how do I know that its 45kg (as opposed to lb or st), and 52lb (as opposed to kg or st)?

Code:
```tokenPtr = strtok( ch, "kg "" "" lb st '\t'" );

while ( tokenPtr != NULL )
{
printf( "&#37;s\n", tokenPtr );
tokenPtr = strtok( NULL, "kg "" "" lb st '\t'" );
}```
That gives the separated numbers, however I still don't know how to determine the units of the weights, or even if I'm going in the right track.

BTW 'm not permitted to use any functions other than getchar(), putchar(), printf(), and the functions from string(3).

Any help is appreciated.

2. Sooooo, you're calculating weight gain (or loss) for multiple users over N days, where the weights can be in varying units for each user?

3. Yes that's right. Each input line signifies a different user. And the various weight records can be in either pounds, kilograms, or stones. BTW 'm not permitted to use any functions other than getchar(), putchar(), printf(), and the functions from string(3).

4. Okie dokie. So you want to be able to convert stones and pounds to kilos for processing. As a first attempt, go for a simple parsing scheme and see if it works. Something like this:
Code:
`sscanf ( line, "%s%d%s%d%s%d", user, &weighta, unita, &weightb, unitb, &days );`

5. Unfortunately, I'm not allowed to use sscanf. Only getchar(), putchar(), printf(), and the functions from string(3).

6. >Unfortunately, I'm not allowed to use sscanf.
Stupid teachers make you do stupid things. How about this then, is it suitably horrendous?
Code:
```/* Be sure to initialize the first character of the arrays to '\0' */
next = strcspn ( line, " \t" );
strncat ( user, line, next );

weighta = strtol ( line + next, &nextp, 0 );
next = strcspn ( nextp, " \t" );

strncat ( unita, nextp, next );

weightb = strtol ( nextp + next, &nextp, 0 );
next = strcspn ( nextp, " \t" );

strncat ( unitb, nextp, next );

days = strtol ( nextp + next, &nextp, 0 );```

7. Originally Posted by Prelude
>Unfortunately, I'm not allowed to use sscanf.
Stupid teachers make you do stupid things. How about this then, is it suitably horrendous?
Code:
```/* Be sure to initialize the first character of the arrays to '\0' */
next = strcspn ( line, " \t" );
strncat ( user, line, next );

weighta = strtol ( line + next, &nextp, 0 );
next = strcspn ( nextp, " \t" );

strncat ( unita, nextp, next );

weightb = strtol ( nextp + next, &nextp, 0 );
next = strcspn ( nextp, " \t" );

strncat ( unitb, nextp, next );

days = strtol ( nextp + next, &nextp, 0 );```
Wow, I'm completely lost on that. I'm a beginner at C, borderline incompetent even. Could you break it down explaining what each line does, please.

8. >Wow, I'm completely lost on that.
Now now, let's skip the flattery.

>Could you break it down explaining what each line does, please.
Okay, let's start with line = "C 14st 80kg 10".

>next = strcspn ( line, " \t" );
If you print &p[next], you'll get " 14st 80kg 10". strcspn essentially finds the first occurrence of any of the characters in the second argument (ie. a tab or a space) and returns the index of that occurrence. In this case, next has a value of 1.

>strncat ( user, line, next );
strcat appends the first argument with N characters from the second argument, where N is specified by the third argument. It copies 1 character from "C 14st 80kg 10" into user, so user becomes "C".

>weighta = strtol ( line + next, &nextp, 0 );
strtol skips whitespace and tries to convert the next non-whitespace characters into an integer. It stores the point where it can't convert anymore in the second argument, and uses the third argument as a radix. Put simply, this guy will store 14 in weighta because after whitespace the string is "14st 80kg 10". It's that way because I added next to line, thus shifting line forward by one character. nextp points to the string "st 80kg 10" because the 's' was the first non-digit character that strtol found.

The rest of the lines just repeat that process until there's nothing left to read.

9. Originally Posted by Prelude
>Wow, I'm completely lost on that.
Now now, let's skip the flattery.

>Could you break it down explaining what each line does, please.
Okay, let's start with line = "C 14st 80kg 10".

>next = strcspn ( line, " \t" );
If you print &p[next], you'll get " 14st 80kg 10". strcspn essentially finds the first occurrence of any of the characters in the second argument (ie. a tab or a space) and returns the index of that occurrence. In this case, next has a value of 1.

>strncat ( user, line, next );
strcat appends the first argument with N characters from the second argument, where N is specified by the third argument. It copies 1 character from "C 14st 80kg 10" into user, so user becomes "C".

>weighta = strtol ( line + next, &nextp, 0 );
strtol skips whitespace and tries to convert the next non-whitespace characters into an integer. It stores the point where it can't convert anymore in the second argument, and uses the third argument as a radix. Put simply, this guy will store 14 in weighta because after whitespace the string is "14st 80kg 10". It's that way because I added next to line, thus shifting line forward by one character. nextp points to the string "st 80kg 10" because the 's' was the first non-digit character that strtol found.

The rest of the lines just repeat that process until there's nothing left to read.
I had to read that a few times before getting the jist of it. Ok from what I understand, this does a good job of separating all the data I need so I can do the calculations. But how do I know that 14 is in stones and not some other weight format (ie. kg or lb), or 80 is in kg (not st or lb)? Unless I'm missing something here...

10. >But how do I know that 14 is in stones and not some other
>weight format (ie. kg or lb), or 80 is in kg (not st or lb)?
You know because you saved that information. In the example previously, weighta is 14. You know it's in stones because unita is "st". You can use strcmp to test that:
Code:
```if ( strcmp ( unita, "st" ) == 0 ) {
/* weighta is in stones, convert it to kilos */
}
else if ( strcmp ( unita, "lb" ) == 0 ) {
/* weighta is in pounds, convert it to kilos */
}
else {
/* You can probably assume that weighta is in kilos, no conversion necessary */
}```

11. Originally Posted by Prelude
>But how do I know that 14 is in stones and not some other
>weight format (ie. kg or lb), or 80 is in kg (not st or lb)?
You know because you saved that information. In the example previously, weighta is 14. You know it's in stones because unita is "st". You can use strcmp to test that:
Code:
```if ( strcmp ( unita, "st" ) == 0 ) {
/* weighta is in stones, convert it to kilos */
}
else if ( strcmp ( unita, "lb" ) == 0 ) {
/* weighta is in pounds, convert it to kilos */
}
else {
/* You can probably assume that weighta is in kilos, no conversion necessary */
}```
Ok, you're awesome! I'm gonna go and try to implement this, which may take a while but at least I know what to do now. Oh, do I declare next as a char, or an array of char (string)? Thanks for your help!

12. >Oh, do I declare next as a char, or an array of char (string)? Thanks for your help!
strcspn returns a size_t, but you can declare next as an int if you don't know about size_t, and nextp as a pointer to char.

13. Well, almost... I'm not allowed to use strtol as it's not in string(3). Back to square 1, I suppose.

14. >I'm not allowed to use strtol as it's not in string(3).
Tell your teacher to come here and explain his reasoning for such an idiotic restriction. If you're going to give an assignment on parsing, you shouldn't restrict away all of the tools for doing it properly. I mean really, you're not going to learn anything useful if you have to skip every reasonable solution that would occur in the real world.

>Back to square 1, I suppose.
Not really. You can use strcat and strcspn to read the numbers as well, but you'll have to manually convert them to integer values because you can't use strtol or atoi.

15. Ye the prof. is insanely evil. If you think that's bad, you should see the amount of validation that has to be done on each line of input. There's 6 pages of specifications! I know the source code for atoi so I can manipulate that a little.

Code:
```#include <stdio.h>
#include <string.h>

int main()
{

int next, i, weighta, weightb, days, nextp;
char line[80], user, c;
char *unita, *unitb;

for(i = 1; (c = getchar()) != '\n'; i++)
{
line[i] = c;
}
line[0] = '\0';
line[i] = '\0';

while (next != NULL)
{
next = strcspn ( line, " \t" );
strncat ( user, line, next );

weighta = strtol ( line + next, &nextp, 0 );
next = strcspn ( nextp, " \t" );

strncat ( unita, nextp, next );

weightb = strtol ( nextp + next, &nextp, 0 );
next = strcspn ( nextp, " \t" );

strncat ( unitb, nextp, next );

days = strtol ( nextp + next, &nextp, 0 );
}
printf("%d\n", weighta);

return 0;
}```
That's what I came up with, it compiles, but there's a slew of warnings and it doesn't work as it should. It'll be a miracle if I survive this course. Any suggestions on why it's not working?

Popular pages Recent additions