How to read multiple files with the same prefix in C? - c

I have a large number of files with prefix info_ _ _ _.txt (eg. info0921,info1231,info0426).
The last four digit is a date with format "mmdd".
I need to write a C program to read all those files, store data into array and do some calculation with it.
I tried to do something like that:
for(i = 0; i < Number_of_files; i++){
sprintf(filename, "info%d.txt", i+1);}
However, it doesn't work well because "i" does not fit the date format.

You have two way :
You try to open all the file that could match your pattern (so approximatively 366 possibility)
You open the directory where your files are, list all the entry, and for each entry, you see if that's match your pattern.
For the first one, something like
for(i = 1; i <= 12; i++) {
for(j = 1; j < 31; j++) {
sprintf(filename, "info%02d%02d.txt", i, j);
if (!file = fopen(filename, "r")) {
if (errno != ENOENT) {
// Real error, log ?
}
} else {
// Yay ! Can use "file" now
  fclose(file);
file = NULL;
}
}
}
will do.
For the last one, we need to know if you're on Linux-like or Windows.

Related

Manipulating files through C to make a top 5 leaderboard

I'm making a connect 4 game as apart of a project and I want to add extra functionality to it, however I'm struggling.
The code is a function I've written that takes a struct (lb leaderboard) and attempts to read the file for that difficulty of all its inputs into a lb array size of 6 which include the difficulty of the bot, turn count and the name of the player, then organizes it in order of turn count and reprints the top 5 back into the file.
I set the first element of the old array to the passed by struct of leaderboard data for the organise function bit.
However, when i set it up with template inputs each separated with a new line like such:
0 7 James
0 13 Jimmy
0 8 Joshua
0 6 Charly
0 9 Jack
It prints this to the file:
0 7 James
0 13 Jimmy
0 8 Joshua
0 6 Charly
(0 9 Jack 1982289408 0 ) same line
The struct looks like this as well{
int turn_count
int diff
char name[20]}
I'm really new to coding so any help is appreciated!
lb old[6];
lb temp;
FILE *file;
if (leaderboard.diff == 0){
file = fopen("leaderboard0.txt", "r+");}
else if (leaderboard.diff == 1){
file = fopen("leaderboard1.txt", "r+");}
else if (leaderboard.diff == 2){
file = fopen("leaderboard2.txt", "r+");}
fseek( file, 1, SEEK_SET );
old[0] = leaderboard;
for (int i = 1; i < 6; i++){
fscanf(file, "%i %i %s", old[i].diff, old[i].turn_count, old[i].name);}
for (int i = 0; i < 6; i++){
for (int j = i + 1; j < 6; j++)
if (old[i].turn_count > old[j].turn_count)
{
temp = old[i];
old[i] = old[j];
old[j] = temp;
}}
for (int i = 0; i < 5; i++){
fprintf(file, "%i %i %s\n", old[i].diff, old[i].turn_count, old[i].name);
fclose(file);}
}
Welcome to StackOverflow, JJewson
First of all, I strongly recommend to you to properly indent your code. One error will be obvious only by that:
lb leaderboard = { ... }; /* was missing from your source code */
lb old[6]; /* you should rename this */
lb temp;
FILE *file;
/* somthing that opens a block was missing from your example source */ {
if (leaderboard.diff == 0) {
file = fopen("leaderboard0.txt", "r+");
} else if (leaderboard.diff == 1) {
file = fopen("leaderboard1.txt", "r+");
} else if (leaderboard.diff == 2) {
file = fopen("leaderboard2.txt", "r+");
}
fseek( file, 1, SEEK_SET );
old[0] = leaderboard;
for (int i = 1; i < 6; i++) {
fscanf(file, "%i %i %s", old[i].diff, old[i].turn_count, old[i].name);
}
for (int i = 0; i < 6; i++) {
for (int j = i + 1; j < 6; j++) {
if (old[i].turn_count > old[j].turn_count) {
temp = old[i];
old[i] = old[j];
old[j] = temp;
}
}
}
for (int i = 0; i < 5; i++) {
fprintf(file, "%i %i %s\n", old[i].diff, old[i].turn_count, old[i].name);
fclose(file); /* this is the obvious error, it should go below this loop */
}
fclose(file); /* here it should be */
}
It would be probably best to rename leaderboard to leader and after that to rename old to leaderboard.
I already mentioned the need to move the fclose call outside of the loop. You close the file after your first write.
I don't understand your first use of fseek( file, 1, SEEK_SET );: Why do you skip the first character of the file? (File offsets start at 0 not 1). Also: After a successfull fopen, the file pointer always points to the beginning of the file.
After you read your leaderboard, you should fclose the file, and reopen it for (over-)writing. (Better keep a backup, if the leader board is important). So: Open "r" for reading, read, close, open "w" for writing, write, close.
You don't do any error checking in your code. You should fix that and inform the user on error and try to handle the error as gracefully as possible. (I.e. do not loose your leaderboard if writing fails.)
Concerning the added part 1982289408 0: Make sure, your new leader (struct `leaderboard´) is properly initialized).
Your algorithm has a slight problem: A new player, who was exactly as good as a previous one, will move that previous one out of the leader board. To fix that, load the file into old[0..4] and put new leader into old[5] (instead of putting him in old[0] and the file content in old[1..5])
Minor imporovement: The sorting loop: for (int i = 0; i < 6; i++) for (int j = i + 1; j < 6; j++)-> thefor iloop only needs to run from 0 .. 4, sofor (int i = 0; i < 5; i++)` would suffice.

Reading from file and storing into 2D dynamically allocated array in C

I searched here, but nothing of what I found seems to work for me. I'm working on a project and I'm trying to implement a function which heading is the following:
read_from_file(*r, *col, fp);
\param *r, where the number of rows of the new matrix is going to be stored;
\param *col where the number of columns of the new matrix is going to be stored;
\param fp the file, already opened in reading mode in the main of the program, where to read from the matrix.
\retval must p, pointer to the new matrix, read from file.
Now, each file is something like:
..........
.......#..
.....####.
.....###..
..........
..........
..........
..........
..........
..........
Basically the program must have the option of uploading a sedimentation area, made of empty cells, the '.', and in case obstacles, the '#'. Some cells can also be full, '*'.
Those "special characters" are also defined like this:
#define EMPTY '.'
#define FULL '*'
#define OBSTACLE '#'
The matrix representing the sedimentation area must be a 2D dynamically allocated array.
I wrote the piece of code about the evaluation of the number of rows and columns and it works just fine. I'm having issue trying to do the following: in the example file I wrote here, the matrix is a 10x10 one, so, one allocated enough space for the matrix:
char**p = malloc(row*sizeof(char*));
int i = 0;
for(i = 0; i < row; i++){
p[i] = malloc(columns*sizeof(char));
What I'm trying to accomplish reading from file is:
p[1][1] = '.';
p[1][2] = '.';
....
p[2][8] = '#';
And so on. I've tried in many ways, but nothing seems to work. Once i try to write this new matrix on file, when I'm trying to read the output, there is an error message display which says that the file is corrupted.
Can anybody help me?
I tried like this:
char*buffer = malloc(sizeof(char));
int k;
while(fgets(buffer, columns, fp) != NULL){
for(i = 0; i < row; i++){
k = 0;
for(j = 0; j < columns; j++){
if(j == col-1)
p[i][j] = buffer[columns - 1]; /* excluding ‘\0’ here */
else
p[i][j] = buffer[k++];
}
}
}
I even tried like this:
While((c = fgetc(fp))) != EOF){
for(i = 0; i < row; i++){
for(j=0; j< columns; j++){
p[i][j] = c;
}
}
}
Thanks in advance.
How did you try to reading from file ? I think you should check out how read a file line by line.
You can try that :
char**p = malloc(row*sizeof(char*));
char *line = NULL
size_t len = 0;
int i = 0;
while ((getline(&line, &len, fp)) != -1) {
p[i++] = strdup(line)
printf("%s", line);
}
I think this should works, didn't test it on compilation, but I think you have the idea !
as someone in the comment said, be careful with getline ! It's not a standard C function and using it will not be portable to non-POSIX systems

Transfer results to txt file C

so i'm completely new to programming (i've been learning for 3 days) and i find myself infront of a problem i simply don't know how to resolve.
I want this program to give me every single combination from 0 to a specific number in base 36. That is easy enough when the number is only about 50000 or so. But my goal from this is to extract actual words(with numbers too) and if i try to get words with 5 characters, the terminal will start overwriting the previous words(not helpful, i want ALL of them).
So i thought i should look for a way to transfer everything into a txt file and there resides my problem: I don't know how... Sorry for the long text but i wanted to explain precisely what i'm trying to get. Thanks for the help.
int main() {
int dec, j, i, q, r, k;
char val[80];
printf("Enter a decimal number: ");
scanf("%d", &dec);
for (k = 0; k <= dec; k++) { /*repeat for all possible combinations*/
q = k;
for (i = 1; q != 0; i++) { /*convert decimal number to value for base 36*/
r = q % 36;
if (r < 10)
r = r + 48;
else
r = r + 55;
val[i] = r;
q = q / 36;
}
for (j = i - 1; j > 0; j--) { /*print every single value*/
printf("%c", val[j]);
}
printf(" "); /*add spaces because why not*/
}
return (0);
}
A few observations that might help:
First is type related:
In your declarations you create the following:
int dec, j, i, q, r, k;
char val[80];
Then later you make the assignment:
val[i] = r;//assigning an int to a char, dangerous
While r is type int with a range (typically) of –2,147,483,648 to 2,147,483,647,
val[i] is of type char with a range (typically) of only –128 to 127.
Because of this, you may run into an overflow, leading to unexpected results.
The most immediate solution is use the same type for both variables. Pick either int or char, but not both.
The other has already been addressed correctly by #Nasim. Use the file version of printf() - fprintf(). As the link shows, the prototype for fprintf() is:
int fprintf( FILE *stream, const char *format [, argument ]...);
Usage example:
FILE *fp = fopen(".\somefile.txt", "w");//create a pointer to a FILE
if(fp)//if the FILE was successfully created, write to it...
{
// some of your previous code...
for (j = i - 1; j > 0; j--)
{ /*print every single value*/
fprintf(fp, "%c", val[j]);//if val is typed as char
//OR
fprintf(fp, "%d", val[j]);//if val is typed as int
}
fclose(fp);
}
Lastly, there are a wide range of methods to perform base conversion. Some more complicated than others.
create a file and then you can use fprintf() instead of printf the only difference between the two is that you need to specify the file as an argument
FILE *myFile = fopen("file.txt", "w"); //"w" erase previous content, "a" appends
If(myFile == NULL) {printf("Error in openning file\n"); exit(1);}
fprintf(myFile, "some integer : %d\n", myInteger); // same as printf my specify file pointer name in first argument
fclose(myFile); //dont forget to close the file

How to read a 10 GB txt file consisting of tab-separated double data line by line in C

I have a txt file consisting of tab-separated data with type double. The data file is over 10 GB, so I just wish to read the data line-by-line and then do some processing. Particularly, the data is layout as an matrix with, say 1001 columns, and millions of rows. Below is just a fake sample to show the layout.
10.2 30.4 42.9 ... 3232.000 23232.45
...
...
7.234 824.23232 ... 4009.23 230.01
...
For each line I'd like to store the first 1000 values in an array, and the last value in a separate variable. I am new to C, so it would be nice if you could kindly point out major steps.
Update:
Thanks for all valuable suggestions and solutions. I just figured out one simple example where I just read a 3-by-4 matrix row by row from a txt file. For each row, the first 3 elements are stored in x, and the last element is stored in vector y. So x is a n-by-p matrix with n=p=3, y is a 1-by-3 vector.
Below is my data file and my code.
Data file:
1.112272 -0.345324 0.608056 0.641006
-0.358203 0.300349 -1.113812 -0.321359
0.155588 2.081781 0.038588 -0.562489
My code:
#include<math.h>
#include <stdlib.h>
#include<stdio.h>
#include <string.h>
#define n 3
#define p 3
void main() {
FILE *fpt;
fpt = fopen("./data_temp.txt", "r");
char line[n*(p+1)*sizeof(double)];
char *token;
double *x;
x = malloc(n*p*sizeof(double));
double y[n];
int index = 0;
int xind = 0;
int yind = 0;
while(fgets(line, sizeof(line), fpt)) {
//printf("%d\n", sizeof(line));
//printf("%s\n", line);
token = strtok(line, "\t");
while(token != NULL) {
printf("%s\n", token);
if((index+1) % (p+1) == 0) { // the last element in each line;
yind = (index + 1) / (p+1) - 1; // get index for y vector;
sscanf(token, "%lf", &(y[yind]));
} else {
sscanf(token, "%lf", &(x[xind]));
xind++;
}
//sscanf(token, "%lf", &(x[index]));
index++;
token = strtok(NULL, "\t");
}
}
int i = 0;
int j = 0;
puts("Print x matrix:");
for(i = 0; i < n*p; i++) {
printf("%f\n", x[i]);
}
printf("\n");
puts("Print y vector:");
for(j = 0; j < n; j++) {
printf("%f\t", y[j]);
}
printf("\n");
free(x);
fclose(fpt);
}
With above, hopefully things will work if I replace data_temp.txt with my raw 10 GB data file (of course change values of n,p, and some other code wherever necessary.)
I have additional questions that I wish if you could help me.
I first initialized char line[] as char line[(p+1)*sizeof(double)] (note not multiplying n). But the line cannot be read completely. How could I assign memory JUST for one single line? What's the lenght? I assume it's (p+1)*sizeof(double) since there are (p+1) doubles in each line. Should I also assign memory for \t and \n? If so, how?
Does the code look reasonable to you? How could I make it more efficient since this code will be executed over millions of rows?
If I don't know the number of columns or rows in the raw 10 GB file, how could I quickly count rows and columns?
Again I am new to C, any comments are very appreciated. Thanks a lot!
1st way
Read file in chunks into preallocated buffer using fread.
2nd way
Map the file into your process memory space using mmap, move the pointer then over the file.
3rd way
Since your file is delimited by lines, open the file with fopen, use setvbuf or similar to set a buffer size greater than about 10 lines or so, then read the file line-by-line using fgets.
To potentially read the file even faster, use open with O_DIRECT (assuming Linux), then use fdopen to get a FILE * for the open file, then use setvbuf to set a page-aligned buffer. Doing that will allow you to bypass the kernel page cache - if your system's implementation works successfully using direct IO that way. (There can be many restrictions to direct IO)
Something to get you started: Reading 1 line
#define COLUMN (1000+1)
double data[COLUMNS];
for (int i = 0; i< COLUMN; i++) {
char delim = '\n';
int cnt = fscanf(in_stream, "%lf%c", &data[i], &delim);
if (cnt < 1) {
if (cnt == EOF && i == 0) return 0; // None read, OK as end of file
puts("Missing or bad data");
return -1; // problem
}
if (delim != '\t') {
// If tab not found, should be at end of line
if (delim == '\n' && i == COLUMN-1) {
return COLUMN; // Success
}
puts("Bad delimiter");
return -1;
}
}
puts("Extra data");
return -1;

C Comparing 2 strings turned into memory allocation issue

Let me start off by saying, I do realize there are a lot of questions with the exact same title, but I didn't find what I was looking for in any of them. I tried to write the following code, in order to errorcheck the user's input, so he wouldn't give 2 variables the same name. Needless to say, it failed, and that is why I am here. While printing the strings I was comparing out as strings, using printf("%s", temp[j].name); was working fine, the character-by-character printing was outputting a series of characters that, from what I know, shouldn't be there. I would like to know what this could all be about, and if there is anyway to solve it, so I can actually compare the 2, without using something from string.h
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#define ARRAYLENGTH 20
typedef struct{
char name[ARRAYLENGTH];
char type[ARRAYLENGTH];
char value[ARRAYLENGTH];
}variable;
int main(){
int amount = 3;
int i, j, k;
variable * varray;
variable * temp;
int flag;
int added = 1;
varray = malloc(amount*sizeof(variable));
if (varray == NULL){
printf("error");
return 1;
}
temp = malloc(amount*sizeof(variable));
if (temp == NULL){
printf("error");
return 1;
}
printf("Give the name of variable # 1 \n");
scanf("%s", varray[0].name);
for (i = 1; i < amount; i++){
flag = 0;
while (flag == 0){
printf("Give the name of variable # %d \n", i + 1);
scanf("%s", temp[i].name);
for (j = 0; j < added; j++){
for (k = 0; temp[i].name[k] != '\0'; k++){
printf("%c,", temp[i].name[k]);
}
printf("\n");
for (k = 0; temp[i].name[k] != '\0'; k++){
if (varray[j].name[k] != temp[i].name[k]){
flag = 1;
break;
}
if (varray[j].name[k] == temp[i].name[k]){
flag = 0;
}
}
}
if (flag == 0){
printf("The variable name you gave already exists, please choose another one. \n");
}
if (flag == 1){
for (j = 0; j < ARRAYLENGTH; j++){
varray[i].name[j] = temp[i].name[j];
}
}
if(flag == 1){
added +=1;
}
}
}
for (i = 0; i < amount; i++){
printf("%s \n", varray[i].name);
}
free(varray);
free(temp);
}
The code compiles without problem, but when I tried to run it, I found that, no matter what my, as a user, input was, the flag would always be 1 in the end. The block of code
printf("\n");
for (k = 0; k < ARRAYLENGTH; k++){
printf("%c,", temp[i].name[k]);
}
printf("\n");
And when the user input is the name John, outputs the following on Visual Studio 2013's Developer command prompt:
Give the name of variable # 1
John
Give the name of variable # 2
John
J,o,h,n,
The variable name you gave already exists, please choose another one.
Give the name of variable # 2
George
G,e,o,r,g,e,
Give the name of variable # 3
George
G,e,o,r,g,e,
G,e,o,r,g,e,
The variable name you gave already exists, please choose another one.
Give the name of variable # 3
John
J,o,h,n,
J,o,h,n,
John
George
John
What I am guessing this problem is about, is that the memory the system is allocating to temp and varray are already being used elsewhere. This errorcheck is crucial for a project I have to do, so I would appreciate any help I can get in solving this problem greatly. Thanks in advance,
LukeSykpe
The problem is with your printing logic.
The scanf function writes the user input into the array, followed by a terminating `\0' character. It does not know the size of your array (20), so it doesn't touch the part of the array that it doesn't actually write.
Instead of this:
for (k = 0; k < ARRAYLENGTH; k++){
write:
for (k = 0; temp[i].name[k] != '\0'; k++) {
Note that you don't need to check for running off the end of the array here. Instead, make sure that the user string is not too big for your array. See this for how to do that.
Edit : This post is not to answer the original question, but to answer a follow-up question posted in comments. I tried to incorporate this into the previous answer, but the owner refused. So here it is.
The problem with your varray comparisons is that, with the code you are showing at least, varray is never initialized. So
if (varray[j].name[k] != temp[i].name[k])
Is a bit like taking a random byte in memory, assigning it to a variable and doing this :
if (RandomByteValue != temp[i].name[k])
Which 90% of the time will be true thus setting your flag to 1.
Essentially, you're missing a
varray[i] = lastVariableGotFromUser
At the end of each main loop.
--- Edit : Added in minor corrections to general functionality ---
Try adding in this :
int added = 1;
Then change this :
for (j = 0; j < amount; j++){
with :
for (j = 0; j < added; j++){
and add in :
if (flag == 1){
// Your for loop
added += 1;
}
What was happening was that you iterated through fields of varray that were uninitialized, and contained random memory. With those modifications (If i didn't forget one, it should work. Try to always limit your loops to only the useful iterations. If you know you only have one variable added in, don't iterate through the 3 fields.
------- Last edit to correct a detail in his code -------
So, your whole :
for (k = 0; temp[i].name[k] != '\0'; k++){
Can be deleted. Now i also know that you don't want to use string.h, However, recoding a strcmp ain't all that complicated. Let's call it
int comp_str(str, str2) // Returns 1 if they don't match, zero if they do.
then just replace your whole for with :
if (comp_str(temp[i].name, varray[j].name) == 0) {
flag = 0;
break;
}
else
flag = 1;
You only want to set the flag when a whole string has been analyzed. So pass it to another function, act upon the return value, and it works! Generally slice your code up. Easier to act/think on. (and also avoids having things like varray[j].name[k] != temp[i].name[k] which is long an not so pleasing to read, in your code.)

Resources