searching data from dat file using c - c

The following program creates a new dat file and stores random data. Then one of the values is searched from the file and printed.
The problem is data up to 13 items are searched and the program exits. As shown in following picture 100 inputs are stored and only 13 items are searched. What is the solution?
// database for storing random values in file and making search operation
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
int rollNo;
int regdNo;
int salary;
int status;
} record;
int main(void) {
int i, n;
record det;
int recordsize = sizeof(det); // size of record
FILE *fp1 = fopen("random.dat", "a+");
if (fp1 == NULL) {
printf("error in opening file : \n");
return 1;
}
printf("enter the no of data to be stored\n");
scanf("%d", &n);
for (i = 0; i < n; i++) {
det.rollNo = rand();
det.regdNo = rand();
det.salary = rand();
det.status = (rand() % 10) + 1;
fwrite(&det, recordsize, 1, fp1);
}
printf("The last roll no of student stored in list: %d\n", det.rollNo);
int stat = 0, countNumber = 0;
record buffer;
int number;
printf("enter the roll number to be searched\n");
scanf("%d", &number);
fseek(fp1, 0, SEEK_SET); // move file position indicator to beginning of file
do {
countNumber++; // counts number of times the file is searched
fread(&buffer, recordsize, 1, fp1);
if (buffer.rollNo == number) {
stat = 1;
break;
}
} while (!feof(fp1));
printf("\n");
if (stat) {
printf("succesfully found at %d\n", countNumber);
printf(" roll number %d\n regd number %d\n salary %d\n status %d\n",
buffer.rollNo, buffer.regdNo, buffer.salary, buffer.status);
} else
printf("there is no such roll number %d in the list\nlength of list : %d\n",
number, countNumber);
fclose(fp1);
}
The output is:

The code works fine on my system, but there are possible problems:
you open the binary file in default mode, possibly text mode. Use "ab+" instead.
the file is open in append mode. If it contains invalid data, especially if its size is not a multiple of the size of the record size, appended records will not be properly aligned, and thus will not be read back correctly.
test the success of the scanf(), fwrite() and fread() calls
the end of file test is incorrect.
Here is an improved version:
// database for storing random values in file and making search operation
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
int rollNo;
int regdNo;
int salary;
int status;
} record;
int main(void) {
int i, n;
record det;
int recordsize = sizeof(det); // size of record
FILE *fp1 = fopen("random.dat", "ab+");
if (fp1 == NULL) {
printf("error in opening file : \n");
return 1;
}
printf("enter the no of data to be stored\n");
if (scanf("%d", &n) != 1)
return 1;
for (i = 0; i < n; i++) {
det.rollNo = rand();
det.regdNo = rand();
det.salary = rand();
det.status = (rand() % 10) + 1;
if (fwrite(&det, recordsize, 1, fp1) != 1) {
perror("cannot write record");
fclose(fp1);
return 2;
}
}
printf("The last roll no of student stored in list: %d\n", det.rollNo);
int stat = 0, countNumber = 0;
record buffer;
int number;
printf("enter the roll number to be searched\n");
if (scanf("%d", &number) != 1)
return 1;
fseek(fp1, 0, SEEK_SET); // move file position indicator to beginning of file
while (fread(&buffer, recordsize, 1, fp1) == 1) {
countNumber++; // counts number of times the file is searched
if (buffer.rollNo == number) {
stat = 1;
break;
}
}
printf("\n");
if (stat) {
printf("successfully found at %d\n", countNumber);
printf(" roll number %d\n"
" regd number %d\n"
" salary %d\n"
" status %d\n",
buffer.rollNo, buffer.regdNo, buffer.salary, buffer.status);
} else {
printf("there is no such roll number %d in the list\n"
"length of list : %d\n",
number, countNumber);
}
fclose(fp1);
return 0;
}

Your main problem is that your store binary data where system expect you to store text, see this question.
fopen(..., "a+"); open a file for text reading, but you store binary data in it
You have some other issues:
You don't use feof the right way
Your main function does not return value,
You do not test the value of n or number.
Corrected code should looks like:
// database for storing random values in file and making search operation
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct
{
int rollNo;
int regdNo;
int salary;
int status;
} record;
int main(void) {
int i, n;
record det;
/* open the file in BINARY mode */
FILE* fp1 = fopen("random.dat" , "a+b");
if (fp1 == NULL) {
printf("error in opening file : \n");
return 1;
}
printf("enter the no of data to be stored\n");
scanf("%d", &n);
for (i = 0; i < n; i++) {
det.rollNo = rand();
det.regdNo = rand();
det.salary = rand();
det.status = (rand() % 10) + 1;
if (1 != fwrite(&det, sizeof det, 1, fp1)) {
perror("fwrite");
return 2;
}
}
printf("The last roll no of student stored in list: %d\n", det.rollNo);
int stat = 0, countNumber = 0;
record buffer;
int number;
printf("enter the roll number to be searched\n");
scanf("%d", &number);
fseek(fp1, 0, SEEK_SET); // rewind would have done the job
while (1) {
countNumber++; // counts number of times the file is searched
if (1 != fread(&buffer, sizeof buffer, 1, fp1)) {
perror("fread");
return 3;
}
if (buffer.rollNo == number) {
stat = 1;
break;
}
}
printf("\n");
if (stat) {
printf("succesfully found at %d\n", countNumber);
printf(" roll number %d\n regd number %d\n salary %d\n status %d\n", buffer.rollNo, buffer.regdNo, buffer.salary, buffer.status);
}
else
printf("there is no such roll number %d in the list\nlength of list : %d\n", number, countNumber);
/* warning: file is not closed in case of error... */
fclose(fp1);
return 0;
}

Related

Saving integers from a file into an array

I was hoping to get a bit of help, I am implementing an inversion counter algorithm to take in 50,000 intergers and display the inversions and time it took to run the algorithm, I am having a hard time allocating and saving the integers from the file into an array. My code complies and runs but nothing happens
here is what I have:
int main(int argc, char** argv)
{
int n, i;
int inversions=0;
int *A;
FILE *file;
char filename[100];
clock_t start, end;
double totalTime;
printf("Enter filename: ");
scanf("%s", filename);
file = fopen(filename, "r");
if(file == NULL)
{
printf("Error opening file!\n");
return 0;
}
fscanf(file, "%d", &n);
A = (int*) malloc(n * sizeof(int));
for(i = 0; i < n; i++) {
fscanf(file, "%d", &A[i]);
}
start = clock();
inversions = countInversionsBruteForce(A, n);
end = clock();
totalTime = (double) (end - start) / CLOCKS_PER_SEC;
printf("Brute Force Algorithm\n");
printf("Number of inversions: %d\n", inversions);
printf("Execution time: %f\n", totalTime);
I think I have noth allocated array size and saved it properly
Your program is incomplete so I was not able to compile it. Minimized the problem to just loading the data into your array:
Formatted code for readability.
Generated a suitable input file. Most likely this is your problem but you have not shared your input sample with us.
Added missing include files.
Remove argc, argv as you not using them.
Minimize scope of variables. Use size_t instead of int for unsigned values.
Max string size on obtaining file name
Check return value for scanf(), fopen(), fscanf().
Printing out the data read to demonstrate it's working.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
printf("Enter filename: ");
char filename[100];
if(scanf("%99s", filename) != 1) {
printf("scanf failed\n");
return 1;
}
FILE *file = fopen(filename, "r");
if(!file) {
printf("Error opening file!\n");
return 1;
}
size_t n;
fscanf(file, "%zu", &n);
if(!n) {
printf("n must be positive");
return 1;
}
int *A = malloc(n * sizeof(*A));
for(size_t i = 0; i < n; i++)
if(fscanf(file, "%d", &A[i]) != 1) {
printf("fscanf() failed\n");
return 1;
}
printf("n = %zu\n", n);
printf("A = ");
for(size_t i = 0; i < n; i++)
printf("%d%s", A[i], i + 1 < n ? ", " : "\n");
}
with 1.txt as:
4
1
2
3
4
a sample session looks like this:
Enter filename: 1.txt
n = 4
A = 1, 2, 3, 4

Why do I get different results depending on how many child processes I use?

I am working on a simple C program that recursively creates children and uses them to sum all the numbers in a file, depending on user input. There are three predetermined file sizes that the user can chose from, as well as three set amounts of children that can be generated. In theory, there could be any number of children or any size file, but for the sake of simplicity there are only 3 here.
The problem I'm running into is, no matter which file I use, the only time the sum is correct is when the program uses only 1 child. With other amounts of children, such as 4, the number is close, but not quite right. Can someone offer me any insight as to what is causing this issue?
Here is the section of code I think is problematic:
// C program to demonstrate use of fork() with pipe()
// By: Maxwell Wendlandt
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <time.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
FILE *file;
int numForks;
// initialize pipes for up to 4 children
int fd[4][2];
// initialize up to 4 processes
pid_t pid[4];
int total = 0;
int finalResult = 0;
char fileName[10] = "";
int fileNum;
int numLines;
// ask which file to scan
printf("Enter file number 1 (1000 nums), 2 (10000 nums) or 3 (100000 nums):\n");
scanf("%i", &fileNum);
// chose the file
switch(fileNum)
{
case 1 :
printf("File 1 selected.\n");
strcpy(fileName, "file1.dat");
numLines = 1000;
break;
case 2 :
printf("File 2 selected.\n");
strcpy(fileName, "file2.dat");
numLines = 10000;
break;
case 3 :
printf("File 3 selected.\n");
strcpy(fileName, "file3.dat");
numLines = 100000;
break;
default :
printf("Enter a valid file number next time.\n");
return 0;
}
// ask how many children (forks)
printf("Do you want 1, 2 or 4 child processes?\n");
scanf("%i", &numForks);
for (int i = 0; i < numForks + 1; i++)
{
if (pipe(fd[i]) == -1)
{
printf("Error with creating pipe.\n");
return 1;
}
}
for(int i = 0; i < numForks; i++)
{
pid[i] = fork();
if(pid[i] == -1)
{
printf("Error creating child.\n");
return 1;
}
if(pid[i] == 0)
{
// children
int sum = 0, num = 0;
int start, end;
file = fopen(fileName, "r");
start = i * (numLines / numForks);
printf("start: %i\n", start);
end = ((i + 1) * (numLines / numForks));
printf("end: %i\n", end);
fseek(file, (start * 4), SEEK_SET);
for(int i = start; i < end; i++)
{
fscanf(file, "%d", &num);
printf("num on line %d is: %d\n", i + 1, num);
sum += num;
}
printf("sum in child: %d\n", sum);
write(fd[i][1], &sum, sizeof(sum));
close(fd[i][1]);
return 0;
}
}
// parent
for(int i = 0; i < numForks; i++)
{
read(fd[i][0], &total, sizeof(total));
close(fd[i][0]);
finalResult += total;
}
printf("The grand total: %i\n", finalResult);
for(int i = 0; i < numForks; i++)
{
wait(NULL);
}
return 0;
}
Thanks in advance!
each line of the file has one 3 digit number on it. So a 1000 number file has 1000 lines.
This means each line consists of four five bytes - the three digits, the carriage return, and the newline character. e.g., 123\r\n. The off-by-two error here
fseek(file, (start * 3), SEEK_SET);
will cause each seek to drift, and each child will read from an earlier position than they should. If each line is five bytes, this should be start * 5.
Aside: I would hazard a guess the numbers in your files are padded with zeroes (see the generation example below).
If so, the fscanf specifier %i may not be desirable, as it acts as strtol with a base of 0, meaning the numbers base is determined by its first characters.
This may lead to confusing results when zero padded numbers are parsed as octal. For example:
004 - octal, value 4.
040 - octal, value 32.
400 - decimal, value 400.
009 - octal, invalid value (0).
011 - octal, value 9.
%d will parse the inputs as base-10 numbers.
This has a few problems.
printf("Do you want 1, 2 or 4 child processes?\n");
scanf("%i", &numForks);
for (int i = 0; i < numForks + 1; i++) {
if (pipe(fd[i]) == -1)
/* ... */
i < numForks + 1 is an off-by-one error. The user can also enter an arbitrary number.This will invoke Undefined Behaviour if fd is accessed via an out-of-bounds index.
In general, you should be checking the return values of more functions, such as scanf, fscanf, fseek, write, and read, to ensure you are working with valid data.
Prefer perror and fprintf(stderr, ...) to print useful error messages to the correct stream.
A very cursory refactoring:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <wait.h>
int main(void)
{
int numForks;
// initialize pipes for up to 4 children
int fd[4][2];
// initialize up to 4 processes
pid_t pid[4];
int total = 0;
int finalResult = 0;
char fileName[10] = "";
int fileNum;
int numLines;
printf("Enter file number 1 (1000 nums), 2 (10000 nums) or 3 (100000 nums):\n");
if (1 != scanf("%d", &fileNum)) {
fprintf(stderr, "Invalid number of files.\n");
return 1;
}
switch (fileNum) {
case 1:
printf("File 1 selected.\n");
strcpy(fileName, "file1.dat");
numLines = 1000;
break;
case 2:
printf("File 2 selected.\n");
strcpy(fileName, "file2.dat");
numLines = 10000;
break;
case 3:
printf("File 3 selected.\n");
strcpy(fileName, "file3.dat");
numLines = 100000;
break;
default:
printf("Enter a valid file number next time.\n");
return 0;
}
printf("Do you want 1, 2 or 4 child processes?\n");
if (1 != scanf("%d", &numForks) || 1 > numForks || numForks > 4 || numForks == 3) {
fprintf(stderr, "Invalid number of child processes.\n");
return 1;
}
for (int i = 0; i < numForks; i++) {
if (pipe(fd[i]) == -1) {
perror("pipe");
return 1;
}
}
for (int i = 0; i < numForks; i++) {
pid[i] = fork();
if (pid[i] == -1) {
perror("fork");
return 1;
}
// children
if (pid[i] == 0) {
int sum = 0, num = 0;
int start, end;
FILE *file = fopen(fileName, "r");
if (!file) {
fprintf(stderr, "Child %d failed to open ", i + 1);
perror(fileName);
return 1;
}
start = i * (numLines / numForks);
end = ((i + 1) * (numLines / numForks));
printf("start: %d\nend: %d\n", start, end);
if (-1 == fseek(file, (start * 4), SEEK_SET)) {
perror("fseek");
return 1;
}
for (int i = start; i < end; i++)
if (1 == fscanf(file, "%d", &num))
sum += num;
printf("sum in child: %d\n", sum);
write(fd[i][1], &sum, sizeof sum);
close(fd[i][1]);
return 0;
}
}
// parent
for (int i = 0; i < numForks; i++) {
if (sizeof total == read(fd[i][0], &total, sizeof total))
finalResult += total;
close(fd[i][0]);
}
for (int i = 0; i < numForks; i++)
wait(NULL);
printf("The grand total: %d\n", finalResult);
}
The code used to generate files to test with (./gen 1000 > file1.dat):
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char **argv)
{
int i = 0;
if (argc > 1)
i = atoi(argv[1]);
srand((unsigned) time(NULL));
while (i-- > 0)
printf("%03d\n", rand() % 1000);
}
And a sanity checker (./sanity-check < file1.dat):
#include <stdio.h>
int main(void)
{
int sum = 0, num;
while (1 == scanf("%d", &num))
sum += num;
printf("%d\n", sum);
}

How to get a file name in C

I am trying to get a file name in C, at my project I will have three files, like Randomic%08d.txt, Inverted%08d.txt and Ordered%08d.txt, where this %08d represents the range I typed, if I typed range equal 3, the file name will be Randomic00000003.txt. I thought to get a file name just returning the file name at the functions in generateFiles.h but I guess that is not good to do because I will have change the return function type.
I want to get the file name because I will need to define a vector with the file data so I need to open the file.
main.c
#include <stdio.h>
#include "generateFiles.h"
int main(void) {
FILE *ordered, *inverted, *randomic;
int range, choice;
char fileName[255];
do {
printf("Menu\n\n");
printf("1 - Criar arquivos \n");
printf("2 - Ordenar usando Bubble Sort \n");
printf("3 - Ordenar usando Insertion Sort \n");
printf("4 - Fechar programa \n");
scanf("%d", &choice);
switch(choice) {
case 1:
printf("Informe o tamanho do arquivo: ");
scanf("%d", &range);
createOrderedFile(range, fileName, ordered);
createInvertedFile(range, fileName, inverted);
createRandomicFile(range, fileName, randomic);
break;
case 2:
fopen("Randomic%08d.txt", "r");
break;
}
} while(choice != 4);
}
generateFiles.h
#include <stdlib.h>
int INDEX = 0;
void createOrderedFile(int range, char fileName[255], FILE *ordered) {
int index;
sprintf(fileName, "Ordered%08d.txt", range);
ordered = fopen(fileName, "w");
for (INDEX = 1; INDEX <= range; INDEX++) {
fprintf(ordered, "%d\n", INDEX);
}
fclose(ordered);
}
void createInvertedFile(int range, char fileName[255], FILE *inverted) {
sprintf(fileName, "Inverted%08d.txt", range);
inverted = fopen(fileName, "w");
for (INDEX = range; INDEX >= 1; INDEX--) {
fprintf(inverted, "%d\n", INDEX);
}
fclose(inverted);
}
void createRandomicFile(int range, char fileName[255], FILE *randomic) {
sprintf(fileName, "Randomic%08d.txt", range);
randomic = fopen(fileName, "w");
for (INDEX = 1; INDEX <= range; INDEX++) {
fprintf(randomic, "%d\n", rand() % range + 1);
}
fclose(randomic);
}

C fork and pipe add numbers from a file

Totals different for same file when executed.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define MAX_FILE_NAME 100
#define RUNS 1
int main() {
int num,i;
FILE *fp;
char*s, buf[1024];
int count =0;
char c;
char filename[MAX_FILE_NAME];
printf("Enter filename: ");
scanf ("%s",filename);
if ((fp =fopen(filename, "r")) == NULL) {
printf("Error");
exit(1);
}
fscanf(fp,"%d",&num);
for (c = getc(fp); c!= EOF; c = getc(fp))
{
if (c == '\n'){
count = count+1;
}
}
printf("%s has %d numbers \n", filename, count);
int f;
printf("Choose from the options how many processes you want to use [1,2,4]: ");
scanf("%i", &f);
printf("%i processes \n", f);
int fds[f+1][2];
int numb[count];
int x,k;
time_t start, finish;
start = time(NULL);
for(i = 0; i < RUNS; i++)
{
pipe(fds[f]);
for( x = 0; x<f; x++)
{
pipe(fds[x]);
int ind[2];
ind[0] = ((x)*(count/f));
ind[1] = ((x+1)*(count/f));
write(fds[x][1], &ind, 2* sizeof(int));
if (fork() ==0)
{
int t =0;
int ind2[2];
read(fds[x][0], &ind2, 2*sizeof(int));
for( k = ind2[0]; k<ind2[1]; k++)
{
t += numb[k];
}
write(fds[f][1], &t, sizeof(int));
exit(0);
}
}
int m, tmp, total;
total = 0;
for( m = 0; m < f; m++)
{
for( m = 0; m < f; m++)
{
read(fds[f][0], &tmp, sizeof(int));
sleep(5);
total += tmp;
}
printf("DOne calc \n");
printf("Total: %i \n", total);
}
finish = time(NULL);
float runtime = (float)((finish-start)/RUNS);
printf("runtime: %f \n", runtime);
fclose(fp);
return 0;
}
You get random result for the same input because the calculation based on uninitialized int numb[count]; values.
According to the C99 standard, section 6.7.8.10:
If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.
Because of it int numb[count]; contains some random junk from memory. To get predictive results use explicit initialization:
#include <string.h> // memset
int numb[count];
memset (numb, 0, sizeof(numb)); // Zero-fills
Use the code bellow to put numbers from filename file into numb:
int i = 0;
char line[1024];
fseek(fp, 0, SEEK_SET);
while(fgets(line, sizeof(line), fp) )
{
if( sscanf(line, "%d", &numb[i]) == 1 ) // One number per line
{
++i;
}
}

Ignoring # comments in the input for a C program

I'm trying to write a program which will read from text files and then output the minimum, maximum and average values. The trouble I am having is ignoring comments in the text files that begin with a hashtag. Here is my working code so far. Can anyone help?
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
char ch, filename[20];
FILE *lun;
int num, min, max, sum, count, first;
printf("Please enter the name of file to load:");
scanf ("%s", filename);
lun=fopen(filename, "r");
if ( lun != NULL)
{
for ( sum= count= first= 0; fscanf( lun, "%d", &num ) == 1; sum += num, ++count )
if ( !first ) { min= max= num; first= 1; }
else if ( num > max ) max= num;
else if ( num < min ) min= num;
fclose( lun );
printf( " Minimum value: %d\n Maximum value: %d\n Average value: %lf\n",
min, max, sum / (double) count );
}
else
printf( "Unable to read file.\n" );
return 0;
}
Read the data in lines (use fgets()).
If the line contains a #, terminate the string there by replacing the '#' with '\0'. Then scan the line for numbers.
See also How to use sscanf() in loops?
And don't forget to check that the file was opened.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char filename[20];
printf("Please enter the name of file to load: ");
if (scanf("%19s", filename) != 1)
{
fprintf(stderr, "Failed to read file name\n");
return 1;
}
FILE *lun = fopen(filename, "r");
if (lun == NULL)
{
fprintf(stderr, "Failed to open file %s for reading\n", filename);
return 1;
}
char line[4096];
int min = 0; // Avoid compilation warnings (may be used uninitialized)
int max = 0; // Ditto
int sum = 0;
int count = 0;
while (fgets(line, sizeof(line), lun) != NULL)
{
char *hash = strchr(line, '#');
if (hash != NULL)
*hash = '\0';
int pos;
int num;
int off = 0;
while (sscanf(line + off, "%d%n", &num, &pos) == 1)
{
if (count == 0)
min = max = num;
if (num > max)
max = num;
if (num < min)
min = num;
sum += num;
count++;
off += pos; // Skip through line
}
}
fclose(lun);
printf("Minimum value: %d\nMaximum value: %d\nAverage value: %lf\n",
min, max, sum / (double)count);
return 0;
}
If your compiler doesn't support C99 or later, you will have to move variable declarations to the start of a block (immediately after a {).
Handling doubles isn't really any harder:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char filename[20];
printf("Please enter the name of file to load: ");
if (scanf("%19s", filename) != 1)
{
fprintf(stderr, "Failed to read file name\n");
return 1;
}
FILE *lun = fopen(filename, "r");
if (lun == NULL)
{
fprintf(stderr, "Failed to open file %s for reading\n", filename);
return 1;
}
char line[4096];
double min = 0.0; // Avoids 'used when uninitialized' warnings
double max = 0.0; // Avoids 'used when uninitialized' warnings
double sum = 0;
int count = 0;
while (fgets(line, sizeof(line), lun) != NULL)
{
char *hash = strchr(line, '#');
if (hash != NULL)
*hash = '\0';
int pos;
double num;
int off = 0;
while (sscanf(line + off, "%lf%n", &num, &pos) == 1)
{
if (count == 0)
min = max = num;
if (num > max)
max = num;
if (num < min)
min = num;
sum += num;
count++;
off += pos; // Skip through line
}
}
fclose(lun);
printf("Minimum value: %f\nMaximum value: %f\nAverage value: %f\n",
min, max, sum / count);
return 0;
}

Resources