I'm trying to read a text file of integers into an array in C.
My text file looks like this: (12 numbers/line)
5713 6936 8764 6702 8535 8654 8710 8332 4948 7627 5472 5311
7331 8719 6135 6672 5786 7113 5292 6923 5683 7020 8595 8606
6837 7003 7415 8372 8429 5726 8323 6442 8672 6488 6932 5884
6811 7785 7189 5531 6323 8561 8283 5114 6669 7217 8307 6599
6961 8695 6026 5580 6010 4954 8725 4955 5532 7736 5372 8712
8343 5375 5931 6449 8223 5844 5931 5307 5436 6405 8599 6302
8617 8222 5985 8216 5044 5259 6523 7526 8702 5878 7559 5366
5362 7393 6633 8781 6289 6470 5342 7278 5348 8677 5779 5763
5718 8678 6406 8774 5931 7324 6819 6393 5027 7545 8385 8795
8277 8059 6362 6980 5899 5828 6707 7149 5621 7287 5958 6506
6517 5710 5504 7070 8797 6840 7112 6063 7014 5419 7514 7431
The actual file has overall 1000 numbers
Currently, I am using the following code:
int main(void){
FILE *my_file;
my_file = fopen("seals.txt", "r");
size_t size = word_conter(my_file);
int weight = 0;
int seals[size];
int i;
for(i = 0; i < size; i++){
if(fscanf(my_file, "%1d", &weight) == 1)
seals[i] = weight;
else break;
}
for(i = 0; i < size; i++){
printf("%d\t %d\n", i, seals[i]);
}
fclose(my_file);
return 0;
}
EDIT: Here is word_counter :
size_t word_conter(FILE *my_file){
if(my_file == NULL){
perror("Error in file");
exit(1);
}
size_t counter = 0, in_word = 0;
char ch;
while((ch = fgetc(my_file)) != EOF){
if(ch == ' ' || ch == '\n' || ch == '\t' || ch == '\0'){
if(in_word){
in_word = 0;
counter++;
}
}
else in_word = 1;
}
return counter;
}
Unfortunately, my output is:
0 245589760
1 1
2 1
3 0
4 -497513808
5 32766
6 245022469
7 1
8 -497513808
9 32766
10 245022268
11 1
12 1
13 0
14 224894352
15 1
16 -497513744
17 32766
18 244994643
19 1
20 245557328
21 1
22 224894352
23 1
24 224879992
25 1
26 0
27 0
28 245584536
29 1
30 245616784
31 1
32 -497513712
33 32766
34 245024298
35 1
36 245557328
37 1
38 -799666472
39 22978
40 -497510096
And so on up to 999.
I have tried using both:
fscanf(my_file, "%1d", &weight) and fscanf(my_file, "%d", &weight)
but both didn't work. I have read the manual about fscanf but still couldn't get it right.
Any help would be much appreciated!
Second Edit:
I used rewind() and that did the trick!
Word counter messes up on edge cases.
Look for transitions from space to non-space. Do not forget to rewind().
size_t counter = 0;
bool in_word = false;
// char ch;
int ch;
while((ch = fgetc(my_file)) != EOF){
if (ch == ' ' || ch == '\n' || ch == '\t' || ch == '\0') { // better: if (isspace(ch)) {
in_word = false;
} else {
if (!in_word) counter++;
in_word = true;
}
}
rewind(my_file); // reset to the beginning
return counter;
Even better, just go through the file twice with the same format.
size_t counter = 0;
while (fscanf(my_file, "%d", &weight) == 1) {
counter++;
}
rewind(my_file); // reset to the beginning
In a nutshell, this is caused by reading through the data file twice in a row without calling rewind().
The problem occurs when fscanf() reads the file for a second time without rewinding. (first read is in word_conter() function):
if(fscanf(my_file, "%d", &weight) == 1)//2nd read on one file descriptor
seals[i] = weight;//program flow never gets here
else break;
This results in execution flow jumping directly to else break;, then continuing to loop on printf() and outputting the uninitialized array: int seals[size];
Also, the function word_conter is susceptible to various inconsistencies data formatting might present within the file it is reading. Suggest stepping through code in a debugger to characterize exactly how this might be contributing to the problem.
My suggestion is to call fopen() and fclose each once inside word_counter, then re-call both again in the main function. This refactored version of size_t word_conter(FILE *my_file) supports this approach. (written to accommodate file containing 12 numbers per line as per mentioned in comments):
size_t word_counter(char *filename)
{
char line[260] = {0};
char *tok = NULL;
int count = 0;
FILE *fp = fopen(filename, "r");
if(fp)
{
while(fgets(line, sizeof(line), fp))
{
tok = strtok(line, " \n");
while(tok)
{
count++;
tok = strtok(NULL, " \n");
}
memset(line, 0, sizeof(line));
}
fclose(fp);
}
return count;
}
Called in main as:
size_t size = word_counter("seals.txt");
There is also an instance of using an incorrect format specifier can invoke undefined behavior
For:
int weight = 0;
The correct format specifier is "%d". Either change:
if(fscanf(my_file, "%1d", &weight) == 1)
to:
if(fscanf(my_file, "%d", &weight) == 1)
or change:
int weight = 0;
int seals[size];
to:
long weight = 0;
long seals[size];
(%ld is used only for long.)
Addressing each of these is likely to address the problem you are seeing. (However, because word_conter() is not shown in detail, there may be other issues.)
try to replace if(fscanf(my_file, "%1d", &weight) == 1) with if(fscanf(my_file, "%d", &weight) != EOF)
Related
I wrote the following code to read the first line of the text file and calculate the size of that string, but the calculated size is 1 number greater than the real size that I see on the text file. For example, this string: SAM, my code calculates the size 4; however the size of SAM is 3 or for Hello my code calculates the size 6, I'm wondering what is wrong with my code:
FILE * inp = fopen("name.txt", "r");
char nameArr[30];
fgets(nameArr, 30, inp);
int i;
for(i = 0; nameArr[i] != '\0' && nameArr[i] != '\n'; ++i)
{
}
if(nameArr[i-1] == ' ')
--i;
printf("i is %d\n", i);
fclose(inp);
I think the problem if here if(nameArr[i-1] == ' ') but I cannot fix it
I suspect you mean
int i; <<<=== i here
for(i = 0; nameArr[i] != '\0' && nameArr[i] != '\n'; ++i)
{
}
if(nameArr[i-1] == ' ')
--i;
printf("i is %d\n", i); <<<=== i here
There must be another i declared somewhere too, in which case change the name of this i something else
I would like to read 3-digit-numbers with spaces inbetween from a file with the fgetc()-command and put them into an array, which is not currently working, as the resulting array has completely different objects in it. What am I doing wrong? (I used a file with "107 313 052 614" in it, resulting in the output "5435 5641 5380 5942")
My Code:
#include<stdio.h>
#include<stdlib.h>
void print_array(int* arrayp, int lengthp){
int i;
for(i=0;i<lengthp;i++){
printf("%i ", arrayp[i]);
}
return;
}
int main(){
int length=1;
int i;
FILE *fp1;
fp1 = fopen("durations.txt", "r");
fgetc(fp1);fgetc(fp1);fgetc(fp1);
while(fgetc(fp1)!=EOF){
length++;
fgetc(fp1);
fgetc(fp1);
fgetc(fp1);
}
fclose(fp1);
int* list = (int*) malloc(sizeof(int)*length);
FILE *fp2;
fp2 = fopen("durations.txt", "r");
for(i=0;i<length;i++){
list[i]=0;
list[i]+=100*(fgetc(fp2));
list[i]+=10*(fgetc(fp2));
list[i]+=(fgetc(fp2));
fgetc(fp2);
}
fclose(fp2);
print_array(list, length);
return 0;
}
The characters that are used to store digits in a "readable" file are not "numbers". The most popular encoding is ascii character encoding, ex. the 1 digit is represented with the number 49 in decimal.
Because the 0, 1, 2 ... 9 digits in ascii encoding are encoded in increasing order, you can just substract 48 (ie. '0' character) to convert a digit character to it's machine format Just - '0'.
Change you loop into:
for(i=0;i<length;i++){
list[i]=0;
list[i]+=100*(fgetc(fp2) - '0');
list[i]+=10*(fgetc(fp2) - '0');
list[i]+=(fgetc(fp2) - '0');
fgetc(fp2);
}
This also explains the current output of your program. If you don't substract '0' from the numbers, then for example for 107 you get:
100 * '1' + 10 * '0' + '7' =
100 * 49 + 10 * 48 + 55 =
5435
The 49, 48 and 55 are decimal values for digits 1, 0 and 7 in ascii table.
The problem is that your are reading in the (probably) ASCII values of each digit and assuming that is the value of the digit. You need to subtract the value of the zero character from each value, like this:
for (i = 0; i < length; i++) {
list[i] = 0;
list[i] += 100 * (fgetc(fp2)-'0');
list[i] += 10 * (fgetc(fp2)-'0');
list[i] += (fgetc(fp2)-'0');
fgetc(fp2);
}
This will work even if your system doesn't use ASCII encoding.
It might be simpler to just read the numbers into cstrings then use the stdlib function atoi to convert each string to a number before loading into the array of ints:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
//open the file for reading with error checking:
FILE* fp;
char c = '0';
int length = 0;
fp = fopen("durations.txt", "r");
if (fp == NULL) {
printf("Could not open file\n");
return 0;
}
//count the number of lines in a EOF-terminated string w/o newlines
for (c = fgetc(fp); c != EOF; c = fgetc(fp)) {
if (c == ' ') {
length += 1;
}
}
rewind(fp); //go back to file end w/o needing close/open
//Then, assuming only spaces are between numbers (so length = length + 1)
char buffer[4];
int* lst = (int*) malloc(sizeof(int)*length);
for (int i = 0; i < length + 1; i++) {
fscanf(fp, "%s", buffer); //stops at spaces
lst[i] = atoi(buffer);
}
//close the file with error checking
int check = fclose(fp);
if (check != 0) {
printf("File close failed");
return 0;
}
for (int i = 0; i < length + 1; i++) { //print result
printf("%d\n", lst[i]);
}
free(lst); //clean up memory after use
lst = NULL;
return 0;
}
number.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14
For instance, I have the above text file. I would like to get the number of integer in the file (which is 14 in this case), and pass every integer into an array.
what I did is
int count = 0;
int i = 0;
while ( 1 )
{
int c = fgetc( fp );
if ( c == EOF || c == '\n' )
{
if ( c == EOF ) break;
}
else if(c ==' ')
{
++count;
--count;
}
else
{
++count;
arr[i] = c-48; // This line give me the wrong number so I subtract it by 48
++i;
}
}
In the case, the count will only get the correct value when there is no double digits number in the file(such as 10 11 12 13). Otherwise, it does not work.
I also tried to use fscanf to store every integer in an array, then get the length of the array. That does not work since I have to define the length of the array first and there is no way I can do that.
My question is how to deal with the double digits number in the file in order to get the right value. Is there a better to do that? Can someone help? Thanks in advance.
You can just read the integers with fscanf(), and every time a number is found, increment a counter.
Something as simple as this could work:
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *fp;
int num;
size_t count = 0;
fp = fopen("somenumbers.txt", "r");
if (!fp) {
fprintf(stderr, "Error reading file\n");
return 1;
}
while (fscanf(fp, "%d", &num) == 1) {
count++;
}
printf("number of digits = %zu\n", count);
fclose(fp);
return 0;
}
I need to make some actions from txt file.
What I was willing to do:
Calculate number of lines in txt
Open txt file
Iterate for each line
Iterate within each line and perform the task within it
Close file
Current code:
int lines(){
FILE *fp = fopen("somedata.txt","r");
int ch;
int count=0;
do{
ch = fgetc(fp);
if( ch== '\n') count++;
}
while( ch != EOF );
fclose(fp);
return count;
}
int main(){
int linesnum=lines();
FILE *fp = fopen("somedata.txt", "r");
if (fp == 0){
fprintf(stderr, "failed to open test0.txt\n");
exit(-1);
}
float areas[linesnum];
for (int j=0;j<linesnum;j++){
float array[150];
for (int i = 0; i < 150; i++){
fscanf(fp, "%f", &array[i]);
if (getc(fp) == (int)'\n'){
//that ends iteration for a line once it founds "\n"
//and assigns its value to temporary array
break;
}
}
//SOME TASK PERFORMED OVER HERE FOR EACH LINE lets say calculate average
if (getc(fp) == EOF){ //That's supposed to be end of iteration through lines
break;
}
}
fclose(fp);
return 0;
}
File is in format (max lines 1000, max elements in each line is 150).
Number of elements in each line is different, so if I make a big matrix of 1000x150, most of elements will be empty; that's why I don't want a matrix, and just simply want perform task for each line.
1 2 3 4 5 6
1 3 2 5 6 7 3 5
1 3 3 2 5 2 3
5 3 4 2 52 5 6
Well, I'm ending up with error Segmentation fault: 11 after performing task for 1st line.
I'm not sure how do I jump to next line after 1st iteration. That's the question.
Any thoughts?
UPDATE: Just had to get rid of EOF break thing in main():
if (getc(fp) == EOF){
break;
}
Something like this may work.
Read a line with fgets. The line must be large enough to hold the longest line.
Use a pointer to line and an offset to parse the values from the line.
%n will receive the number of bytes used by the sscanf so the pointer can be advanced to the next value.
char line[1500];
char *pline;
int used = 0;
int i = 0;
int linesnum = 0;
float array[150];
while ( fgets ( line, sizeof ( line), fp))) {
pline = line;
i = 0;
while ( ( sscanf ( pline, "%f%n", &array[i], &used)) == 1) {
pline += used;
i++;
if ( i >= 150) {
break;
}
}
linesnum++;
if ( linesnum >= 1000) {
break;
}
}
12 23 34 45 56
34 23 56 21 43
12 57 98 34 12
The above is the content of a txt file.
With C, i can use fgetc(myFile) to get the first integer and store into an integer variable.
I will check whether it is 12.
if it is 12, i want to replace with 25. How do i exactly replace it a certain number.
How do i rewrite a certain part of it?
Or do i store every number into an array, replace all 12s with another numbers and overwrite the whole file??
Save result to another file, than renames it. This code opens homework.txt, replaces all 12 -> 25 and writes result to homework_new.txt
#include <stdio.h>
#include <string.h>
#define MAXBUF 42
#define HOMEWORKFILE "homework.txt"
#define HOMEWORKNEWFILE "homework_new.txt"
int main(int argc, char **argv)
{
char buf[MAXBUF+1];
char str[MAXBUF+1];
FILE *hw;
FILE *hw_new;
int length;
int i, j;
int number;
char is_first;
int n_line = 0;
hw = fopen(HOMEWORKFILE, "r");
hw_new = fopen(HOMEWORKNEWFILE, "w");
if (!hw)
{
fprintf(stderr, "File not found: %s\n", HOMEWORKFILE);
return 5;
}
while(!feof(hw))
if (fgets(buf, MAXBUF, hw) != NULL)
{
length = strlen(buf);
j = 0;
str[0] = 0;
is_first = 1;
n_line++;
/* parse string */
for(i = 0; i < strlen(buf); ++i)
{
if (isblank(buf[i]) || buf[i] == '\0' || buf[i] == '\n')
{
str[j] = 0;
number = atoi(str);
if (is_first)
is_first = 0;
else
fprintf(hw_new, " ");
if (number == 12)
fprintf(hw_new, "%d", 25);
else
fprintf(hw_new, "%d", number);
j = 0;
}
else if (isdigit(buf[i]))
{
str[j++] = buf[i];
}
else
{
fprintf(stderr, "bad input on line %d '%s'\n", n_line, buf);
return 100;
}
}
fprintf(hw_new, "\n");
}
fclose(hw_new);
fclose(hw);
return 0;
}
If it's a small file , to overwrite the whole file is a good idea and easier.
If it's not limited in c language, you can try powerful tools like "sed" or some script language.
Here's a list:
fgets
ftell
fseek
fputs
Note that you need to ensure the correct lengths of the data written, in order to overwrite exactly what you want.
Another option would be, as you said, to overwrite the whole file, then you also need
freopen