Read header of PPM file only using getchar() - c

I need to read a PPM file but I'm limited to only using getchar() but I'm running into trouble ignoring whitespaces.
I'm using num=num*10+(ch-48); to read the height and width but don't know how to read them all at once while ignoring spaces and '\n' or comments.
I use this to read the magic number:
int magic;
while(magic==0){
if (getchar()=='P') //MAGIC NUMBER
magic=getchar()-48;
}
printf("%d\\n",magic);
i used this function to read the height and width which works only when the data in the header is seperated only by '\n'
int getinteger(int base)
{ char ch;
int val = 0;
while ((ch = getchar()) != '\\n' && (ch = getchar()) != '\\t' && (ch = getchar()) != ' ')
if (ch \>= '0' && ch \<= '0'+base-1)
val = base\*val + (ch-'0');
else
return ERROR;
return val;
}
this is the part in main()
height=getinteger(10);
while(height==-1){
height=getinteger(10);
}

Comparing "magic" with 0 is undefined behaviour since it's not initialized yet (so it's basically just a chunk of memory):
int magic; // WARNING: We don't know exact value, may be not 0
while(magic==0){
if (getchar()=='P') //MAGIC NUMBER
magic=getchar()-48;
}
Consider initializing variable before comparing:
int magic = 0; // We know that magic will be defined as 0
while (magic == 0) {
if (getchar() == 'P') // MAGIC NUMBER
magic = getchar() - 48;
}
In this function:
int getinteger(int base)
{ char ch;
int val = 0;
while ((ch = getchar()) != '\\n' && (ch = getchar()) != '\\t' && (ch = getchar()) != ' ')
if (ch \>= '0' && ch \<= '0'+base-1)
val = base\*val + (ch-'0');
else
return ERROR;
return val;
}
(I'm assuming that ERROR = -1, is that correct?) In your condition getchar() will work 3 times, not 1 (since it calls getchar() for putting in ch every check). Rewrite it to call only once for saving in variable ch. Another problem occurs when first symbol will be whitespace, not digit. In this case val will remains 0 (since while loop will be skipped), so returned value will also be '0'. To avoid this, you can check value of val and return ERROR, when it is not changed:
int getinteger(int base) {
char ch = getchar(); // get only one char and save for later use
int val = 0; // 0 means not changed
while (ch != '\n' && ch != '\t' && ch != ' ') {
if (ch >= '0' && ch <= '0' + base - 1)
val = base * val + (ch - '0');
else
return ERROR;
ch = getchar(); // get a new char for next loop iteration and checking if it is digit
}
if (val == 0) // val was not changed
return ERROR; // loop in "main" will be continued
else
return val; // val was changed, return it
}
UPD: We can also use this fact to simplify our function a lot:
int getinteger(int base) {
char ch = getchar(); // get only one char and save for later use
int val = 0; // 0" means not changed
while (ch >= '0' && ch <= '0' + base - 1) { // if "ch" is not a number, this loop will be skipped
val = base * val + (ch - '0');
ch = getchar(); // get a new char for next loop iteration and checking if it is digit
}
if (val == 0) // val was not changed (ch was not a number)
return ERROR; // loop in "main" will be continued
else
return val; // val was changed, return it
}
And last but not least, remove extra '' before symbols (\\n -> \n, \>= -> >= etc.) if they present in your code.
Combining everything above results in something like this:
#include <stdio.h>
#define ERROR -1
int getinteger(int base) {
char ch = getchar(); // get only one char and save for later use
int val = 0; // 0 means not changed
while (ch >= '0' && ch <= '0' + base - 1) { // if "ch" is not a number, this loop will be skipped
val = base * val + (ch - '0');
ch = getchar(); // get a new char for next loop iteration and checking if it is digit
}
if (val == 0) // val was not changed (ch was not a number)
return ERROR; // loop in "main" will be continued
else
return val; // val was changed, return it
}
int main() {
// dunno what's before
int magic = 0; // We know that magic will be defined as 0
while (magic == 0) {
if (getchar() == 'P') // MAGIC NUMBER
magic = getchar() - 48;
}
printf("magic = %d\n", magic);
int height = getinteger(10);
while (height == -1)
height = getinteger(10);
printf("height = %d\n", height);
// dunno what's after
}
Result:
$ echo " \n P3 #blabla \n 34 \t " | ./a.out
magic = 3
height = 34

Related

why does only the 1st file reading function executes over multiple programs of the same kind in C language?

This code contains 3 file handling related functions which read from a file named "mno". But only the 1st called function in the main() is working. If the 1st function of the list is commented then, only the 2nd function will work and the third won't. Same goes for the 3rd one
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
void countVowel(char fin[])
{
FILE *fl;
char ch;
int count = 0;
fl = fopen(fin, "r");
while (ch != EOF)
{
ch = tolower(fgetc(fl));
count += (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') ? 1 : 0;
}
fclose(fl);
printf("Number of Vowels in the file \" %s \"-> \t %d \n", fin, count);
}
void countConsonant(char fin[])
{
FILE *fl;
char ch;
int count = 0;
fl = fopen(fin, "r");
while (ch != EOF)
{
ch = tolower(fgetc(fl));
count += (!(ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') && (ch >= 'a' && ch <= 'z')) ? 1 : 0;
}
fclose(fl);
printf("Number of Consonant in the file \" %s \"-> \t %d \n", fin, count);
}
void countAlphabet(char fin[])
{
FILE *fl;
char ch;
int count = 0;
fl = fopen(fin, "r");
while (ch != EOF)
{
ch = tolower(fgetc(fl));
count += (ch >= 'a' && ch <= 'z') ? 1 : 0;
}
fclose(fl);
printf("Number of Alphabets in the file \" %s \"-> \t %d \n", fin, count);
}
int main()
{
countVowel("mno"); // output -> 10
countConsonant("mno"); // output -> 0
countAlphabet("mno"); // output -> 0
return 0;
}
Here are the contents of "mno" file ->
qwertyuiopasdfghjklzxcvbnm, QWERTYUIOPASDFGHJKLZXCVBNM, 1234567890
As others have mentioned, your handling of EOF was incorrect:
ch was uninitialized on the first loop iteration
Doing tolower(fgetc(fl)) would obliterate the EOF value.
Using char ch; instead of int ch; would allow a [legitimate] 0xFF to be seen as an EOF.
But, it seems wasteful to have three separate functions to create the three different counts because the most time is spent in the I/O versus the determination of what type of character we're looking at. This is particularly true when the counts are so interelated.
We can keep track of multiple types of counts easily using a struct.
Here's a refactored version that calculates all three counts in a single pass through the file:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
struct counts {
int vowels;
int consonants;
int alpha;
};
void
countAll(const char *fin,struct counts *count)
{
FILE *fl;
int ch;
int vowel;
count->vowels = 0;
count->consonants = 0;
count->alpha = 0;
fl = fopen(fin, "r");
if (fl == NULL) {
perror(fin);
exit(1);
}
while (1) {
ch = fgetc(fl);
// stop on EOF
if (ch == EOF)
break;
// we only care about alphabetic chars
if (! isalpha(ch))
continue;
// got one more ...
count->alpha += 1;
ch = tolower(ch);
// is current character a vowel?
vowel = (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u');
// since we know it's alphabetic, it _must_ be either a vowel or a
// consonant
if (vowel)
count->vowels += 1;
else
count->consonants += 1;
}
fclose(fl);
printf("In the file: \"%s\"\n",fin);
printf(" Number of Vowels: %d\n",count->vowels);
printf(" Number of Consonants: %d\n",count->consonants);
printf(" Number of Alphabetics: %d\n",count->alpha);
}
int
main(void)
{
struct counts count;
countAll("mno",&count);
return 0;
}
For your given input file, the program output is:
In the file: "mno"
Number of Vowels: 10
Number of Consonants: 42
Number of Alphabetics: 52
You are using ch uninitialized. at while (ch != EOF). Every function call after the first has ch equal to 0 at the start, because you forgot to initialize it and the memory was set to -1 before. You can fix it by replacing the loops like this:
int ch;
...
while ((ch = fgetc(fl)) != EOF)
{
ch = tolower(ch);
count += ...;
}
Here ch is getting initialized before you check it and later converted to lowercase.
EDIT:
Note that this only works if ch is an int, so it can handle the value of -1 (EOF) and the byte 255 is not truncated to -1.
EDIT:
At first I said ch was 0 all the time. It was -1. I am so sorry, I swapped it with the null terminator, which is usually the reason for such behavior.

Kernighan and Ritchie - exercise 3.3 (expand function)

I have solved the exercises 3.3 from the K&R book. The solution I have implemented seems to work, but is a bit verbose and there could be smarter way to write this code. I wanted to ask if there could be problems with the solution I implemented and if there were easier way to write it:
Write a function expand(s1,s2) that expands shorthand notations like
a-z in the string s1 into the equivalent complete list abc...xyz in
s2. Allow for letters of either case and digits, and be prepared to
handle cases like a-b-c and a-z0-9 and -a-z. Arrange that a leading or
trailing - is taken literally
My code is this one:
#include <stdio.h>
void expand(char s1[],char s2[]){
int j=0,i=0;
while(s1[j] != '\0'){
if (s1[j]>= 'a' && s1[j] <= 'z' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= 'a' && s1[j+2] <= 'z' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (s1[j]>= 'A' && s1[j] <= 'Z' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= 'A' && s1[j+2] <= 'Z' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (s1[j]>= '0' && s1[j] <= '9' && s1[j+1] == '-' && s1[j+1]!='\0' && s1[j+2] >= '0' && s1[j+2] <= '9' && s1[j+2] !='\0'){
int z = s1[j+2]-s1[j];
int c;
for (c=0;c<=z;c++){
s2[i]= c+s1[j];
i++;
}
j=j+3;
}
else if (j!= 0 && s1[j] == '-' && (s1[j-1] < s1[j+1])){
int z = s1[j+1]-(1+s1[j-1]);
int c;
for (c=0;c<=z;c++){
s2[i]= c+(s1[j-1]+1);
i++;
}
j=j+2;
}
else if ( s1[j]>= 32 && s1[j] <= 127 && (s1[j+1] != '-' || s1[j+1]>= 32 && s1[j+1] <= 127 )){
s2[i] = s1[j];
j++;
i++;
}
}
s2[i]='\n';
i++;
s2[i]='\0';
}
int main() {
int c;
char s2[100];
expand("-a-c,a-c-g,A-Z0-9--", s2);
printf("%s",s2);
}
The code works in this way:
First it check if there is a triplet of the kind "x-y" where x<y. Then if gives to the array the values from x to y included and jump to the next character after the triplet "x-y". The same is done for upper case letters and for numbers in further if conditions.
the condition else if (j!= 0 && s1[j] == '-' && (s1[j-1] < s1[j+1])) is used to check for cases like "a-c-d1". The code I have implemented in this example will work like this:
Since we start with the 0-th character in "a-c-d" and the pattern "x-y" is present, "abc" will be assigned to the array. then we will directly jump to the second - in "a-c-f". Since this second - is preceded by a letter "c" and followed by a letter "f", and "c"<"f", then the characters between "c" and "f" will be assigned to the array, excluding the initial "c". Then the index for the string will jump of two and reach 1.
Some other way :
you only to know the last char before - and if it is the same type as current one (lower or upper case letter or digit)
when you get a - and previous char is a letter or digit you know you may have to make expansion
if you have a letter or digit after - and it is corresponding to letter/digit before - you know you can expand from char before / to current one.
you do need to look forward but only save previous char and char before -
you do same kind of processing for each different char type (letter/digit)
You can find an example after :
#include <stdio.h>
// handle different char type
typedef enum E_chartype {
LowerCaseLetter,
UpperCaseLetter,
Digit09,
OtherChar
} E_chartype;
// save if we may have a posdible expansion
typedef enum E_states {
NothingStarted,
StartedExpansion
} E_states;
// find type of a char
E_chartype getCharType(char c) {
if ((c >= 'a') && (c <= 'z'))
return LowerCaseLetter;
if (( c >= 'A') && (c <= 'Z'))
return UpperCaseLetter;
if ((c >= '0') && (c <= '9'))
return Digit09;
return OtherChar;
}
void expandopt(char *inS, char *outS) {
// init output string to null string
outS[0] = 0;
char *endS = outS;
E_states automat = NothingStarted;
char savedChar = 0;
int currentIndex;
E_chartype prevCType=OtherChar,savedCType=OtherChar;
char savedC = 0,prevC=0;
// loop on input string
for (currentIndex = 0; inS[currentIndex] != 0;currentIndex++) {
// save current char in variable c for shorter writting
char c = inS[currentIndex];
printf("%c : ",c);
// save type of current char
E_chartype currentCType = getCharType(c);
switch (automat) {
// genersl case notjing yet started
case NothingStarted:
// possibkee expansion if previous chsr is letter or digit and current char is -
if ((prevCType != OtherChar) && (c == '-')) {
printf("start rep\n");
automat = StartedExpansion;
// save the previous char and its type as it eill br the reference fircexpansion
savedCType = prevCType;
savedC = prevC;
} else {
// reset and cooy current char to iutput
automat = NothingStarted;
printf("nothing\n");
*endS++ = c;
}
break;
case StartedExpansion:
// we make ecpansion only if still same char type and letter/digit is strictly after saved one
if ((currentCType == savedCType) && (c > savedC)){
printf("expansion ");
for (char newC
= savedC+1;newC <= c;newC++) {
*endS++ = newC;
}
// save char in case thrre id a - after, which mean nee expansion
savedC = c;
} else {
// save current chsrcsnd its type
savedCType = currentCType;
savedC = c;
// copy previous char (= -) whch was not vopief in case of expansion
*endS++ = prevC;
*endS++ = c;
}
automat = NothingStarted;
break;
}
// save current chsr and type
prevCType = currentCType;
prevC = c;
}
// add 0 at end of string
*endS = 0;
}
int main() {
expandopt("-a-c,a-c-g,A-Z0-9–",s2);
printf("%s\n",s2);
}
Sorry for the code formatting, I did not find good code editor on phone.

Validating user input between 1-9 using getchar()

Hey guys im trying to write a small program where the user has to put in a number between 1-9, anything else is an error, but I'm having trouble validating the input because if you put 12 it only reads the 1 and it goes in the loop. It has to be done using getchar() this is what have so far:
printf(%s,"please enter a number between 1 - 9);
int c;
c = getchar();
while(c != '\n') {
int count = 1;
count ++;
if ((c >= '0' && c <= '9') || count > 1) {
printf(%s, "Congrats!);
}
else
{
print(%s, "ERROR);
}
}
I'm also having problems validating the char into an int after it goes in. If i put in 5 i get 53.
Try changing count > 1 to count == 1, and initialize it to 0 rather than 1. That way you can keep count of the number of digits you have. Also, note that because you initialize count to 1 and then immediately increment it, count > 1 will always evaluate to true, so if you gave it any char it will always say it's correct.
getchar() will return the next character typed. If you want more than the first character you will need a call getchar() again within the while loop.
//Somewhere to store the result
//initialized with an invalid result value
int digitchar = 0;
//Get the first char
int c = getchar();
while (c != '\n')
{
//Check if we already have a digit
//and that the new char is a digit
if (digitchar == 0 && c >= '1' && c <= '9')
{
digitchar = c;
}
//Get the next char
c = getchar();
}
//Check if we have stored a result
if (digitchar != 0)
{
//Success
}
Note this doesn't handle if a non-digit or newline character is entered. You would need to handle that as an error as well as if more than one digit is entered.
This is not working with 12 because getchar() takes one character per time.The following example is one way to solve it.
printf("please enter a number between 1 - 9");
int c[10], count=1;
//Declare an array because user may insert a bigger number
char number;
//This loop allow the user to enter an input
for(i=0;i<10;i++){
number = getchar();
if (number != ' '){
c[i] = atoi(number);
sum = sum + c[i];
}
else if(number == ' '){
//Terminate the loop when user stop typing
break;
}
else if( sum > 9 || sum < 0){
//Start loop again until user enter valid input
printf("You entered a number out of field try again\n");
continue;
}
}
while(c != '\n') {
count ++;
if ((c >= '0' && c <= '9') || count > 1) {
printf("%d Congrats!",c);
}
else
{
printf(" %d ERROR", c);
}
}
Remember that getchar() returns the ascii value of the char, thus when you pass the value to the function you must subtract char '0' to pass the actual decimal value into the function.
Another point is that you must clear the input buffer. If your user enters wrong input, you have to make sure that there is nothing left on the input buffer before you try to read input again.
Hope this helps.
int main(void) {
int input = 0; // 0 is the sentinel value to close program
printf("\n%s\n", "Enter value between 1-9 .\nEnter [0] to finish.");
do {
input = getchar();
if (((input>= '1') && (input <= '9') || input == '0') && getchar() == '\n') {
if ((input >= '1') && (input <= '9')) {
callYourOwnFuntionAndPassValue(input - '0');
printf("\n%s\n", "Enter value between 1-9 .\nEnter [0] to finish.");
}
}
else {
while (getchar() != '\n') {} // clear input buffer
printf("\n%s\n", "Please enter a valid number");
}
} while (input != END_PROGRAM);
return NO_ERROR; // NO_ERROR = 0
}

C: character comparison fails

After succesfully running an entabulator, my detabulator won't pick up on a character comparison that should exit a while loop. After trying "0(tab)8(enter)(ctrl+D)" as input the tab is written correctly as spaces, but after rp is incremented to point to the 8, the while loop that should read the 8 won't exit and I get a seg fault. Here's the code:
#include <string.h>
#include <stdio.h>
#define MAXLINE 100
char doc[9001];
main(int argc, char *argv[])
{
int max = 0;
char *rp = doc;
char *wp = rp;
char *tf = wp;
char *lp = doc;
while ((*(rp++) = getchar()) != EOF);
*--rp = '\0';
rp = doc;
j = 0;
while ( (*rp != '\0') && (argc == 1)) {
if (*rp == '\n') {
lp = rp + 1;
*wp++ = *rp++;
}
while( (*rp != '\t') && (*rp != '\0') && (*rp != '\n') ) { /*this loops after a tab*/
*wp++ = *rp++;
}
if (*rp == '\t') {
rp++;
tf = lp + ((((wp - lp) / 8) + 1) * 8);
while ((tf - wp) != 0)
*wp++ = 's';
}
}
if (*rp == '\0')
*wp = '\0';
printf("%s\n", doc);
}
There are some as yet unexplored problems with the initial input loop.
You should never risk overflowing a buffer, even if you allocate 9001 bytes for it. That's how viruses and things break into programs. Also, you have a problem because you are comparing a character with EOF. Unfortunately, getchar() returns an int: it has to because it returns any valid character value as a positive value, and EOF as a negative value (usually -1, but nothing guarantees that value).
So, you might write that loop more safely, and clearly, as:
char *end = doc + sizeof(doc) - 1;
int c;
while (rp < end && (c = getchar()) != EOF)
*rp++ = c;
*rp = '\0';
With your loop as written, one of two undesirable things happens:
if char is an unsigned type, then you will never detect EOF.
if char is a signed type, then you will detect EOF when you read a valid character (often ÿ, y-umlaut, LATIN SMALL LETTER Y WITH DIAERESIS, U+00FF).
Neither is good. The code above avoids both problems without needing to know whether plain char is signed or unsigned.
Conventionally, if you have an empty loop body, you emphasize this by placing the semicolon on a line on its own. Many an infinite loop has been caused by a stray semicolon after a while condition; by placing the semicolon on the next line, you emphasize that it is intentional, not accidental.
while ((*(rp++) = getchar()) != EOF);
while ((*(rp++) = getchar()) != EOF)
;
What I feel is, the below loop is going into infinite loop.
while( (*rp != '\t') && (*rp != '\0') && (*rp != '\n') ) { /*this loops after a tab*/
*wp++ = *rp++;
This is because, you are checking for rp!= '\t' and so on, but here
if (*rp == '\t')
{
rp++;
tf = lp + ((((wp - lp) / 8) + 1) * 8);
while ((tf - wp) != 0)
*wp++ = 's';
}
you are filling the doc array with char 's' and which is over writing '\t' also, so the above loop is going to infinite.

Printing Uppercase/Lowercase letters

I'm doing a program that is asking the user to enter a stream of characters and printing out the number of uppercase and lowercase letters. I'm trying to do it with a function, but having some trouble printing it..for every character input im entering im getting 0, 0
Would appreciate your help to understand what am I doing wrong:
#include <stdio.h>
#include <ctype.h>
int case_letters(int ch);
int main(void)
{
int x;
printf("please enter a some characters, and ctrl + d to see result\n");
case_letters(x);
return 0;
}
int case_letters(int ch)
{
int numOfUpper = 0;
int numOfLower = 0;
while ((ch = getchar()) != EOF)
{
if ((ch = isdigit(ch)) || ch == '\n')
{
printf("please enter a valid character\n");
continue;
}
else if ((ch = isupper(ch)))
{
numOfUpper++;
}
else if ((ch = islower(ch)))
{
numOfLower++;
}
}
return printf("%d, %d", numOfUpper, numOfLower);
}
All of your if statements assign different value to ch and do not check ch's value.
For example, if you enter a correct char, this
if ((ch = isdigit(ch)) || ch == '\n')
will assign 0 to ch, because isdigit(ch) will return 0. I guess you need
if ( isdigit(ch) || ch == '\n')
Same for islower and isupper.
if ((ch = isdigit(ch)) || ch == '\n')
^-- assignment, not equality test.
You're trashing the value of ch with the return value of isdigit(), and isupper(), and islower(), so that the original user-entered value is destroyed as soon as you do the isdigit test.
Try
if (isdigit(ch) || ch == '\n')
else if (isupper(ch))
else if (islower(ch))
instead. No need to preserve the iswhatever values.

Resources