EDIT:
complete code with main is here http://codepad.org/79aLzj2H
and once again this is were the weird behavious is happening
for (i = 0; i<tab_size; i++)
{
//CORRECT OUTPUT
printf("%s\n", tableau[i].capitale);
printf("%s\n", tableau[i].pays);
printf("%s\n", tableau[i].commentaire);
//WRONG OUTPUT
//printf("%s --- %s --- %s |\n", tableau[i].capitale, tableau[i].pays, tableau[i].commentaire);
}
I have an array of the following strcuture
struct T_info
{
char capitale[255];
char pays[255];
char commentaire[255];
};
struct T_info *tableau;
This is how the array is populated
int advance(FILE *f)
{
char c;
c = getc(f);
if(c == '\n')
return 0;
while(c != EOF && (c == ' ' || c == '\t'))
{
c = getc(f);
}
return fseek(f, -1, SEEK_CUR);
}
int get_word(FILE *f, char * buffer)
{
char c;
int count = 0;
int space = 0;
while((c = getc(f)) != EOF)
{
if (c == '\n')
{
buffer[count] = '\0';
return -2;
}
if ((c == ' ' || c == '\t') && space < 1)
{
buffer[count] = c;
count ++;
space++;
}
else
{
if (c != ' ' && c != '\t')
{
buffer[count] = c;
count ++;
space = 0;
}
else /* more than one space*/
{
advance(f);
break;
}
}
}
buffer[count] = '\0';
if(c == EOF)
return -1;
return count;
}
void fill_table(FILE *f,struct T_info *tab)
{
int line = 0, column = 0;
fseek(f, 0, SEEK_SET);
char buffer[MAX_LINE];
char c;
int res;
int i = 0;
while((res = get_word(f, buffer)) != -999)
{
switch(column)
{
case 0:
strcpy(tab[line].capitale, buffer);
column++;
break;
case 1:
strcpy(tab[line].pays, buffer);
column++;
break;
default:
strcpy(tab[line].commentaire, buffer);
column++;
break;
}
/*if I printf each one alone here, everything works ok*/
//last word in line
if (res == -2)
{
if (column == 2)
{
strcpy(tab[line].commentaire, " ");
}
//wrong output here
printf("%s -- %s -- %s\n", tab[line].capitale, tab[line].pays, tab[line].commentaire);
column = 0;
line++;
continue;
}
column = column % 3;
if (column == 0)
{
line++;
}
/*EOF reached*/
if(res == -1)
return;
}
return ;
}
Edit :
trying this
printf("%s -- ", tab[line].capitale);
printf("%s --", tab[line].pays);
printf("%s --\n", tab[line].commentaire);
gives me as result
-- --abi -- Emirats arabes unis
I expect to get
Abu Dhabi -- Emirats arabes unis --
Am I missing something?
Does printf have side effects?
Well, it prints to the screen. That's a side effect. Other than that: no.
is printf changing its parameters
No
I get wrong resutts [...] what is going on?
If by wrong results you mean that the output does not appear when it should, this is probably just a line buffering issue (your second version does not print newline which may cause the output to not be flushed).
It's highly unlikely that printf is your problem. What is far, far more likely is that you're corrupting memory and your strange results from printf are just a symptom.
There are several places I see in your code which might result in reading or writing past the end of an array. It's hard to say which of them might be causing you problems without seeing your input, but here are a few that I noticed:
get_lines_count won't count the last line if it doesn't end in a newline, but your other methods will process that line
advance will skip over a newline if it is preceded by spaces, which will cause your column-based processing to get off, and could result in some of your strings being uninitialized
get_word doesn't do any bounds checks on buffer
There may be others, those were just the ones that popped out at me.
I tested your code, adding the missing parts (MAX_LINE constant, main function and a sample datafile with three columns separated by 2+ whitespace), and the code works as expected.
Perhaps the code you posted is still not complete (fill_table() looks for a -999 magic number from get_word(), but get_word() never returns that), your main function is missing, so we don't know if you are properly allocating memory, etc.
Unrelated but important: it is not recommended (and also not portable) to do relative movements with fseek in text files. You probably want to use ungetc instead in this case. If you really want to move the file pointer while reading a text stream, you should use fgetpos and fsetpos.
Your approach for getting help is very wrong. You assumed that printf had side effects without even understanding your code. The problem is clearly not in printf, but you held information unnecessarily. Your code is not complete. You should create a reduced testcase that compiles and displays your problem clearly, and include it in full in your question. Don't blame random library functions if you don't understand what is really wrong with your program. The problem can be anywhere.
From your comments, i am assuming if you use these printf statements,
printf("%s\n", tableau[i].capitale);
printf("%s", tableau[i].pays);
printf("%s\n", tableau[i].commentaire);
then everything works fine...
So try replacing your single printf statement with this. (Line no. 173 in http://codepad.org/79aLzj2H)
printf("%s\n %s %s /n", tableau[i].capitale, tableau[i].pays, tableau[i].commentaire);
Related
I am trying to read in a file that contains digits operated by commas and store them in an array without the commas present.
For example: processes.txt contains
0,1,3
1,0,5
2,9,8
3,10,6
And an array called numbers should look like:
0 1 3 1 0 5 2 9 8 3 10 6
The code I had so far is:
FILE *fp1;
char c; //declaration of characters
fp1=fopen(argv[1],"r"); //opening the file
int list[300];
c=fgetc(fp1); //taking character from fp1 pointer or file
int i=0,number,num=0;
while(c!=EOF){ //iterate until end of file
if (isdigit(c)){ //if it is digit
sscanf(&c,"%d",&number); //changing character to number (c)
num=(num*10)+number;
}
else if (c==',' || c=='\n') { //if it is new line or ,then it will store the number in list
list[i]=num;
num=0;
i++;
}
c=fgetc(fp1);
}
But this is having problems if it is a double digit. Does anyone have a better solution? Thank you!
For the data shown with no space before the commas, you could simply use:
while (fscanf(fp1, "%d,", &num) == 1 && i < 300)
list[i++] = num;
This will read the comma after the number if there is one, silently ignoring when there isn't one. If there might be white space before the commas in the data, add a blank before the comma in the format string. The test on i prevents you writing outside the bounds of the list array. The ++ operator comes into its own here.
First, fgetc returns an int, so c needs to be an int.
Other than that, I would use a slightly different approach. I admit that it is slightly overcomplicated. However, this approach may be usable if you have several different types of fields that requires different actions, like a parser. For your specific problem, I recommend Johathan Leffler's answer.
int c=fgetc(f);
while(c!=EOF && i<300) {
if(isdigit(c)) {
fseek(f, -1, SEEK_CUR);
if(fscanf(f, "%d", &list[i++]) != 1) {
// Handle error
}
}
c=fgetc(f);
}
Here I don't care about commas and newlines. I take ANYTHING other than a digit as a separator. What I do is basically this:
read next byte
if byte is digit:
back one byte in the file
read number, irregardless of length
else continue
The added condition i<300 is for security reasons. If you really want to check that nothing else than commas and newlines (I did not get the impression that you found that important) you could easily just add an else if (c == ... to handle the error.
Note that you should always check the return value for functions like sscanf, fscanf, scanf etc. Actually, you should also do that for fseek. In this situation it's not as important since this code is very unlikely to fail for that reason, so I left it out for readability. But in production code you SHOULD check it.
My solution is to read the whole line first and then parse it with strtok_r with comma as a delimiter. If you want portable code you should use strtok instead.
A naive implementation of readline would be something like this:
static char *readline(FILE *file)
{
char *line = malloc(sizeof(char));
int index = 0;
int c = fgetc(file);
if (c == EOF) {
free(line);
return NULL;
}
while (c != EOF && c != '\n') {
line[index++] = c;
char *l = realloc(line, (index + 1) * sizeof(char));
if (l == NULL) {
free(line);
return NULL;
}
line = l;
c = fgetc(file);
}
line[index] = '\0';
return line;
}
Then you just need to parse the whole line with strtok_r, so you would end with something like this:
int main(int argc, char **argv)
{
FILE *file = fopen(argv[1], "re");
int list[300];
if (file == NULL) {
return 1;
}
char *line;
int numc = 0;
while((line = readline(file)) != NULL) {
char *saveptr;
// Get the first token
char *tok = strtok_r(line, ",", &saveptr);
// Now start parsing the whole line
while (tok != NULL) {
// Convert the token to a long if possible
long num = strtol(tok, NULL, 0);
if (errno != 0) {
// Handle no value conversion
// ...
// ...
}
list[numc++] = (int) num;
// Get next token
tok = strtok_r(NULL, ",", &saveptr);
}
free(line);
}
fclose(file);
return 0;
}
And for printing the whole list just use a for loop:
for (int i = 0; i < numc; i++) {
printf("%d ", list[i]);
}
printf("\n");
I'm writing a program for school that asks to read text from a file, capitalizes everything, and removes the punctuation and spaces. The file "Congress.txt" contains
(Congress shall make no law respecting an establishment of religion, or prohibiting the free exercise thereof; or abridging the freedom of speech, or of the press; or the right of the people peaceably to assemble, and to petition the government for a redress of grievances.)
It reads in correctly but what I have so far to remove the punctuation, spaces, and capitalize causes some major problems with junk characters. My code so far is:
void processFile(char line[]) {
FILE *fp;
int i = 0;
char c;
if (!(fp = fopen("congress.txt", "r"))) {
printf("File could not be opened for input.\n");
exit(1);
}
line[i] = '\0';
fseek(fp, 0, SEEK_END);
fseek(fp, 0, SEEK_SET);
for (i = 0; i < MAX; ++i) {
fscanf(fp, "%c", &line[i]);
if (line[i] == ' ')
i++;
else if (ispunct((unsigned char)line[i]))
i++;
else if (islower((unsigned char)line[i])) {
line[i] = toupper((unsigned char)line[i]);
i++;
}
printf("%c", line[i]);
fprintf(csis, "%c", line[i]);
}
fclose(fp);
}
I don't know if it's an issue but I have MAX defined as 272 because that's what the text file is including punctuation and spaces.
My output I am getting is:
C╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠Press any key to continue . . .
The fundamental algorithm needs to be along the lines of:
while next character is not EOF
if it is alphabetic
save the upper case version of it in the string
null terminate the string
which translates into C as:
int c;
int i = 0;
while ((c = getc(fp)) != EOF)
{
if (isalpha(c))
line[i++] = toupper(c);
}
line[i] = '\0';
This code doesn't need the (unsigned char) cast with the functions from <ctype.h> because c is guaranteed to contain either EOF (in which case it doesn't get into the body of the loop) or the value of a character converted to unsigned char anyway. You only have to worry about the cast when you use char c (as in the code in the question) and try to write toupper(c) or isalpha(c). The problem is that plain char can be a signed type, so some characters, notoriously ÿ (y-umlaut, U+00FF, LATIN SMALL LETTER Y WITH DIAERESIS), will appear as a negative value, and that breaks the requirements on the inputs to the <ctype.h> functions. This code will attempt to case-convert characters that are already upper-case, but that's probably cheaper than a second test.
What else you do in the way of printing, etc is up to you. The csis file stream is a global scope variable; that's a bit (tr)icky. You should probably terminate the output printing with a newline.
The code shown is vulnerable to buffer overflow. If the length of line is MAX, then you can modify the loop condition to:
while (i < MAX - 1 && (c = getc(fp)) != EOF)
If, as would be a better design, you change the function signature to:
void processFile(int size, char line[]) {
and assert that the size is strictly positive:
assert(size > 0);
and then the loop condition changes to:
while (i < size - 1 && (c = getc(fp)) != EOF)
Obviously, you change the call too:
char line[4096];
processFile(sizeof(line), line);
in the posted code, there is no intermediate processing,
so the following code ignores the 'line[]' input parameter
void processFile()
{
FILE *fp = NULL;
if (!(fp = fopen("congress.txt", "r")))
{
printf("File could not be opened for input.\n");
exit(1);
}
// implied else, fopen successful
unsigned int c; // must be integer so EOF (-1) can be recognized
while( EOF != (c =(unsigned)fgetc(fp) ) )
{
if( (isalpha(c) || isblank(c) ) && !ispunct(c) ) // a...z or A...Z or space
{
// note toupper has no effect on upper case characters
// note toupper has no effect on a space
printf("%c", toupper(c));
fprintf(csis, "%c", toupper(c));
}
}
printf( "\n" );
fclose(fp);
} // end function: processFile
Okay so what I did was created a second character array. My first array read in the entire file. I created a second array which would only take in alphabetical characters from the first array then make them uppercase. My correct and completed function for that part of my homework is as follows:
void processFile(char line[], char newline[]) {
FILE *fp;
int i = 0;
int j = 0;
if (!(fp = fopen("congress.txt", "r"))) { //checks file open
printf("File could not be opened for input.\n");
exit(1);
}
line[i] = '\0';
fseek(fp, 0, SEEK_END); //idk what they do but they make it not crash
fseek(fp, 0, SEEK_SET);
for (i = 0; i < MAX; ++i) { //reads the file into the first array
fscanf(fp, "%c", &line[i]);
}
for (i = 0; i < MAX; ++i) {
if (isalpha(line[i])){ //if it's an alphabetical character
newline[j] = line[i]; //read into new array
newline[j] = toupper(newline[j]); //makes that letter capitalized
j++;
}
}
fclose(fp);
}
Just make sure that after creating the new array, it will be smaller than your defined MAX. To make it easy I just counted the now missing punctuation and spaces (which was 50) so for future "for" loops it was:
for (i = 0; i < MAX - 50; ++i)
I have file that will contain either two numbers with variable whitespace in between them or just a blank line. I need to know when the input is just a blank line, and then to not assign into those variables using fscanf.
Doing:
FILE *pFile = fopen (my_file, "r");
if (pFile == NULL) perror ("Error opening file");
int succ = 1, num1 = 0, num2 = 0;
while (succ != EOF)
{
succ = fscanf(pFile, "%d %d", &num1, &num2);
}
Works very well for detecting all of the numbers properly, but not the newline.
I tried:
fscanf(pFile, "%d %d %*[^\n]", &num1, &num2);
But it always properly assigns to both numbers. I want to be able to make a switch statement to do other logic based on if succ is 2 (indicating both numbers were assigned too) or 0 (indicating a blank line).
I'd prefer avoiding getline if possible; seems like that would be inefficient to use iostream stuff mixed with stdio functions.
I tried this guys suggestion, didn't work out, although logically I thought it would work, it didn't.
Honestly, I don't even understand why something like
"%d %d \n"
Wouldn't work. It's simple... scan the file until a newline, return back how many assignments were done, that's it!
"%d %d \n" will not work to achieve OP goals as any white-space directive consumes any white-space. Using ' ' or '\n' make no difference they both consume 0 or more white-spaces. (expect in '[]' specifiers)
OP wants to detect a '\n' and fscanf() is not a good choice. fgets() is better.
#define N (100 /* longest line */)
char buf[N];
while (fgets(buf, sizeof buf, pFile) != NULL) {
int cnt = sscanf("%d%d",&num1, &num2);
switch (cnt) {
case 0: Handle_NoNumbers(); break;
case 1: Handle_1Number(); break;
case 2: Handle_Success(); break;
}
}
The '%d' in fscanf(stream, "%d", &num) specifies to consume all leading white-space before a number without regard to '\n' or ' ', etc.
If code must use fscanf(), then code needs to consume leading white-space before calling fscanf(... "%d") in a way to distinguish '\n' from ' '.
// Consume white space except \n and EOF
int consume_ws(FILE *pFile) {
do {
int c = fgetc(pFile); // could use fscanf(...%c...) here
if (c == '\n' || c == EOF) return c;
} while (isspace(c));
ungetc(c, pFile); // put the char back
return 0;
}
...
while(1) {
int num[2];
int i;
for (i = 0; i < 2; i++) {
if (consume_ws(pFile)) {
Handle_ScantData(); // Not enough data
return;
}
if (1 != fscanf(pFile, "%d", &num[i]) {
Handle_NonnumericData();
return;
}
}
int ch = consume_ws(pFile);
if (ch == 0) Handle_ExtraData();
// Else do something with the 2 numbers
if (ch == EOF) return;
}
So I have a program that takes user input and compares it to a specific line in a file, however the final line will always be credited as incorrect, so can someone solve this for me?, thanks.
File content (just a list of random words)
Baby
Milk
Car
Face
Library
Disc
Lollipop
Suck
Food
Pig
(libraries are stdio,conio and string)
char text[100], blank[100];
int c = 0, d = 0;
void space(void);
int main()
{
int loop = 0;
char str[512];
char string[512];
int line = 1;
int dis = 1;
int score = 0;
char text[64];
FILE *fd;
fd = fopen("Student Usernames.txt", "r"); // Should be test
if (fd == NULL)
{
printf("Failed to open file\n");
exit(1);
}
do
{
printf("Enter the string: ");
gets(text);
while (text[c] != '\0')
{
if (!(text[c] == ' ' && text[c] == ' '))
{
string[d] = text[c];
d++;
}
c++;
}
string[d] = '\0';
printf("Text after removing blanks\n%s\n", string);
getch();
for(loop = 0;loop<line;++loop)
{
fgets(str, sizeof(str), fd);
}
printf("\nLine %d: %s\n", dis, str);
dis=dis+1;
str[strlen(str)-1] = '\0';
if(strcmp(string,str) == 0 )
{
printf("Match\n");
score=score+2;
}
else
{
printf("Nope\n");
score=score+1;
}
getch();
c=0;
d=0;
}
while(!feof(fd));
printf("Score: %d",score);
getch();
}
For any input on the last line, the output will always be incorrect, I believe this is something to do with the for loop not turning it into the next variable, but seeing as the <= notation makes this program worse, I really just need a simple fix for the program thanks.
Some observations:
You must never use gets (it is not even in the C11 standard anymore). Instead of gets(text) use fgets(text, sizeof(text), stdin) – this way a long input will not overflow the text array.
There will be stuff printed at the end because you don't check the return value of either the gets or the fgets, so when end of file occurs for either the file or for user input the rest of that iteration still runs. fgets returns NULL if it didn't read anything – check for that instead of using feof.
You remove newlines from the file input but not from the user input, so the comparison will always fail when you switch from gets to fgets (which doesn't strip linefeeds). The second (otherwise pointless) comparison of text[c] against ' ' should be against '\n'.
edit: Also, in case the last line of your file does not end in a linefeed, the comparison will fail on the last line because you don't check if the last character is a linefeed before you remove it.
The for (loop = 0; loop < line; ++loop) -loop is pointless because line is always 1, so the body is only executed once.
You have unnecessarily global variables which the program hard to follow. And, for instance, your local text[64] overshadows the global text[100], so if you think you are modifying the global buffer, you are not. If your code is complete, none of the variables should be global.
The function getch() is non-standard. There is no easy direct replacement, so you may just accept that you are not writing portable code, but it's something to be aware of.
I am new to C programming and I am currently trying to teach myself how to create a C program that can count words and lines in the input stream and print the two totals to the standard output.
What I am actually trying to do is to have the program count the number of lines and count the number of words depending on the definition of a word in which I feel that I am off.
I want the words to exclude blanks, tabs, newlines, hyphens, or colons. While having the program output the results (words and lines) as decimals.
#include<stdio.h>
int main()
{
int iochar;
int words;
int lines;
printf("Enter something here:\n\n");
while ((iochar = getchar ()) !=EOF)
{
if((iochar == ' ') || (iochar == '\t') || (iochar == '\n'))
putchar(iochar);
}
return 0;
}
Am I totally off on this program?
If your question is how to fix the compile error, that's simple. Add one more closing brace at the end.
But your program will still do only one pass through the loop and will print only one character if and only if the user types a space, tab or newline. No matter what the user types, the program will then terminate. I doubt that's what you wanted.
I suspect this is what you intended:
while ((iochar = getchar ()) !=EOF)
{
if((iochar == ' ') || (iochar == '\t') || (iochar == '\n'))
{
putchar(iochar);
}
}
return 0;
After your "I am trying to have thee numbers be right justified in an 8-column field ..." I cannot understand what you are trying to say :(
int words = 0;
int lines = 0;
char buffer[1024];
while(fgets(buffer, sizeof buffer, stdin))
{
lines++;
if(buffer[0] == '\n')
continue;
char *tmp = buffer-1;
while(tmp = strchr(tmp+1, ' '))
words++;
words++; /* count last word before \0*/
}
printf("lines: %d, words: %d\n", lines, words);
is that what you need/want?
The error message is:
Test.c:20:1: error: expected declaration or statement at end of input
It does not compile because you are missing a }.
Had you properly indented your code, like so, you would have found your mistake:
#include<stdio.h>
int main() {
int iochar;
int words;
int lines;
printf("Enter something here:\n\n");
while ((iochar = getchar ()) !=EOF)
{
if((iochar==' ')||(iochar=='\t')||(iochar=='\n'))
{
putchar(iochar);
iochar = getchar();
}
return 0;
}
Yet another example of the importance of readability :)