How to group lines of data using fscanf - c

I've been having trouble with a program. The first number in the file is the number of "sets" the program needs to run. For every set, I need it to read the next 6 integers, split into two groups. For example, with this data set:
{3, 4, 6, 8, 5, 5, 7, 9, 8, 7, 2, 4, 5, 6, 9, 1, 5, 7, 6}
The program should run 3 sets. In set one, it needs to read the next three integers in one group, then the next three integers in the other group, then move to the next set. So
Set 1:
[4, 6, 8] = 18
[5, 5, 7] = 17
Set 2:
[9, 8, 7] = 24
[2, 4, 5] = 11
Set 3:
[6, 9, 1] = 16
[5, 7, 6] = 18
I'm honestly not sure where to start. I've managed to get it to read the first number as the set number, but I'm not sure how to group the integers the way I need it to.
Here's the code so far:
int main(){
int set_number, set_total, points_1, points_2;
int point_total_1 = 0, point_total_2 = 0;
FILE * ifp;
char input_name[50] = "";
printf("Please enter file input name for scores.\n\n");
scanf("%s", &input_name);
ifp = fopen(input_name, "r");
fscanf(ifp, "%d", &set_total);
FILE * ofp = fopen("ninja_scores_output.txt", "w");
for(set_number = 1; set_number <= set_total; set_number++){
fprintf(ofp,"Set #%d:\n", set_number);
fprintf(ofp,"Student 1: %d points.\n\n", points_1);
fprintf(ofp,"Student 2: %d points.\n\n", points_2);
point_total_1 += points_1;
point_total_2 += points_2;
}
fprintf(ofp,"Final Scores:\n"
"Student 1: %d total points.\n\n", point_total_1);
fprintf(ofp,"Student 2: %d total points.\n\n", point_total_2);
if(point_total_1 > point_total_2){
fprintf(ofp, "Student 1 will move onto the next round!");
}
else if(point_total_2 > point_total_1){
fprintf(ofp, "Student 2 will move onto the next round!");
}
fclose(ofp);
fclose(ifp);
return 0;
}
EDIT:
After some tinkering, I managed to get it to read the lines properly, but something's still going wrong. How do I get it to continue down the lines after it finishes it's first loop? In the test file, the values are: {3, 3, 3, 19, 16, 8, 9, 15, 1, 14, 12, 13, 1, 7, 8, 9, 6, 6, 5} (All on new lines). When I run the program, it runs three sets, but in the first set, it's listing student 1 as earning 65 points, while student 2 earned 15 points. In subsequent sets, student 1's score only increases by 6 and student 2 only increases by 15. I'm not sure how to fix it.
Here's the new code:
int main(){
int set_number, set_total, point_val, points_1, points_2;
int point_total_1 = 0, point_total_2 = 0;
FILE * ifp;
char input_name[50] = "";
printf("Please enter file input name for scores.\n\n");
scanf("%s", &input_name);
ifp = fopen(input_name, "r");
FILE * ofp = fopen("ninja_scores_output.txt", "w");
fscanf(ifp, "%d", &set_total);
int current_throw;
int throw_value;
for(set_number = 1; set_number <= set_total; set_number++){
for(current_throw = 1; current_throw <= 6; current_throw++){
fscanf(ifp, "%d", &point_val);
if(current_throw <= 3){
points_1 += current_throw;
}
else{
points_2 += current_throw;
}
}
fprintf(ofp,"Set #%d:\n", set_number);
fprintf(ofp,"Student 1: %d points.\n\n", points_1);
fprintf(ofp,"Student 2: %d points.\n\n", points_2);
point_total_1 += points_1;
point_total_2 += points_2;
}
fprintf(ofp,"Final Scores:\n"
"Student 1: %d total points.\n\n", point_total_1);
fprintf(ofp,"Student 2: %d total points.\n\n", point_total_2);
if(point_total_1 > point_total_2){
fprintf(ofp, "Student 1 will move onto the next round!");
}
else if(point_total_2 > point_total_1){
fprintf(ofp, "Student 2 will move onto the next round!");
}
fclose(ofp);
fclose(ifp);
return 0;
}

If I am following along correctly and you need to read the number of sets from the first line, then each set of six values from the lines that follow (one number per-line), producing the output you show of "Set X" followed by the two groups of three values, then your logic shown isn't going to get you there.
This sort of number of sets, groups within each set and number of groups is one that can be handled fairly simply with nested loops. To read the number of sets, you simply read the first value and then you will loop that number of times. That sets the limits for your outer loop for you. For example to read and setup your outer loop you could do:
#include <stdio.h>
#define SETSZ 6 /* if you need a constant, #define one (or more) */
#define GRPSZ 3
#define NGRPS 2
int main (int argc, char **argv) {
int nsets = 0; /* array holding set */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (fscanf (fp, "%d", &nsets) != 1) { /* read/VALIDATE nsets */
fputs ("error: invalid format - nsets not read.\n", stderr);
return 1;
}
for (int i = 0; i < nsets; i++) { /* loop nsets times */
int set[SETSZ] = {0}; /* array holding set */
/* process remaining sets here */
}
(note: when taking user input, you must VALIDATE every INPUT by checking the return of the input function used. Otherwise, you are just inviting Undefined Behavior in your code on a matching or input failure)
As you loop over each set, you need to:
fill the set with SETSZ values validating each input,
loop NGRPS times processing GRPSZ values from the set,
find the sum of the GRPSZ values, and
output the data in the format shown
In doing so, you can replace the /* process remaining sets here */ placeholder with:
for (int j = 0; j < SETSZ; j++) { /* loop SETSZ times filling set */
if (fscanf (fp, "%d", &set[j]) != 1) { /* read/VALIDATE set value */
fputs ("error: invalid format in set.\n", stderr);
return 1;
}
}
printf ("\nSet %d\n\n", i + 1); /* output set number */
for (int j = 0; j < NGRPS; j++) { /* loop NGRPS times */
int sum = 0; /* declare sum for group of 3 */
putchar ('['); /* output '[' prefix */
for (int k = 0; k < GRPSZ; k++) { /* loop GRPSZ times */
sum += set[j * GRPSZ + k]; /* sum group values */
printf (k ? ", %d" : "%d", set[j * GRPSZ + k]); /* output values */
}
printf ("] = %d\n", sum); /* output result */
}
Other than closing your file, that is the extent of your program. Note, I am simply outputting values to stdout. If you want to write the values to a file, you can simply redirect the output to an output file, e.g. ./program infile > outfile, or you can open another file stream for output and write the values to the file from within the program -- entirely up to you.
Note the program read from the filename provided as the first argument (or reads from stdin by default if no argument is given). The complete example would be:
#include <stdio.h>
#define SETSZ 6 /* if you need a constant, #define one (or more) */
#define GRPSZ 3
#define NGRPS 2
int main (int argc, char **argv) {
int nsets = 0; /* array holding set */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (fscanf (fp, "%d", &nsets) != 1) { /* read/VALIDATE nsets */
fputs ("error: invalid format - nsets not read.\n", stderr);
return 1;
}
for (int i = 0; i < nsets; i++) { /* loop nsets times */
int set[SETSZ] = {0}; /* array holding set */
for (int j = 0; j < SETSZ; j++) { /* loop SETSZ times filling set */
if (fscanf (fp, "%d", &set[j]) != 1) { /* read/VALIDATE set value */
fputs ("error: invalid format in set.\n", stderr);
return 1;
}
}
printf ("\nSet %d\n\n", i + 1); /* output set number */
for (int j = 0; j < NGRPS; j++) { /* loop NGRPS times */
int sum = 0; /* declare sum for group of 3 */
putchar ('['); /* output '[' prefix */
for (int k = 0; k < GRPSZ; k++) { /* loop GRPSZ times */
sum += set[j * GRPSZ + k]; /* sum group values */
printf (k ? ", %d" : "%d", set[j * GRPSZ + k]); /* output values */
}
printf ("] = %d\n", sum); /* output result */
}
}
if (fp != stdin) /* close file if not stdin */
fclose (fp);
}
Input File
With input from the numbers under your "EDIT:", you would have:
$ cat dat/setsofsix.txt
3
3
3
19
16
8
9
15
1
14
12
13
1
7
8
9
6
6
5
Example Use/Output
The program takes the filename to read as the first argument to the program:
$ ./bin/readsetsofsix dat/setsofsix.txt
Set 1
[3, 3, 19] = 25
[16, 8, 9] = 33
Set 2
[15, 1, 14] = 30
[12, 13, 1] = 26
Set 3
[7, 8, 9] = 24
[6, 6, 5] = 17
Let me know if I understood your problem and whether you have further questions. I'm more than happy to help further.

Related

Make an array multiply only the columns behind it, going one at a time until the last one and save the answer in c

I'm making a program in C that factors any number using primes and saves these primes, multiplying them you find all the divisors of a number.
But I can't make an array that multiplies the previous columns and saves the results. follow the example
60 / 2
30 / 2
15 / 3
5 / 5
divisors = 2, 2, 3, 5
now i need`add 1 to array array {1, 2, 2, 3, 5}
i need this now start colune 2 {1, 2} 2 * 1 = 2 save.
next colune 3 {1, 2, 2} 2 * 1 = 2 but we already have 2 so don't save it.
continue 2 * 2 = 4 save.
colune 4 {1, 2, 2, 3} 3 * 1 = 3 save, 3 * 2 = 6 save, 3 * 4 = 12 save.
colune 5 {1, 2, 2, 3, 5} 5 * 1 = 5 save, 5* 2 = 10, 5 * 4 = 20 save, 5 * 3= 15 save, 5 * 6 = 30 save, 5 * 12 = 60 save.
now we found all divisors of 60 = 1, 2, 3, 4, 5, 6, 10 ,12 , 15,20, 30, 60.
It is important to mention that I need the program to be like this, I know there are other ways... but I only need this one, I have been unable to complete it for 1 week
video to help https://www.youtube.com/watch?v=p0v5FpONddU&t=1s&ab_channel=MATEM%C3%81TICAFORALLLUISCARLOS
my program so far
#include <stdlib.h>
#include <stdio.h>
int N = 1;
int verificarPrimo(int numero);
int main()
{
int num = 60, i, primo = 1, resultados[N], j = 1;
for (i = 0; i < 60; i++)
{
if (primo == 1)
{
resultados[N - 1] = primo;
i = 2;
primo = i;
}
if (verificarPrimo(i))
{
while (num % i == 0)
{
num = num / i;
resultados[N] = i;
N++;
}
}
}
for (i = 1; i < N; i++)
{
printf("%d \n", resultados[i]);
}
}
int verificarPrimo(int primo)
{
int i;
if (primo <= 1)
return 0;
for (i = 2; i <= primo / 2; i++)
{
if (primo % i == 0)
return 0;
}
return 1;
}
I tried out your code and ran into some issues with how the results were being stored. First off, the results array is being initially defined as an array with a size of "1", and that it not what you probably want.
int num = 60, i, primo = 1, resultados[N], j = 1;
With that in mind and determining the spirit of this project, following is tweaked version of the code to test for one or more values and their factors.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int verificarPrimo(int primo)
{
int sq = sqrt(primo) + 1; /* Usual checking for a prime number is from '2' to the square root of the number being evaluated */
if (primo <= 1)
return 0;
for (int i = 2; i < sq; i++)
{
if (primo % i == 0)
return 0;
}
return 1;
}
int main()
{
int N = 0;
int num = 0, entry = 0, resultados[100]; /* The results array needs to be defined with some value large enough to contain the assorted factors a number might have */
printf("Enter a number to evaluate for factors: "); /* Using a prompt to allow various values to be tested */
scanf("%d", &entry);
num = entry;
if (verificarPrimo(num)) /* Catchall in case the entered number is a prime number */
{
printf("This number is a prime number and has no factors other than one and itself\n");
return 0;
}
resultados[0] = 1; /* Normally the value '1' is implied in a list of factors, so these lines could be omitted */
N = 1;
for (int i = 2; i < entry; i++)
{
if (verificarPrimo(i))
{
while (num % i == 0)
{
num = num / i;
resultados[N] = i;
N++;
}
}
}
printf("Factors for %d\n", entry);
for (int i = 0; i < N; i++)
{
printf("%d ", resultados[i]);
}
printf("\n");
return 0;
}
Some items to point out in this tweaked code.
In the prime number verification function, it is usually customary to set up a for loop in testing for prime numbers to go from the value of "2" to the square root of the number being tested. There usually is no need travel to one half of the number being tested. For that, the #include <math.h> statement was added (FYI, "-lm" would need to be added to link in the math library).
Instead of defining the results array with a value of one element, an arbitrary value of "60" was chosen for the holding the possible number of results when evaluating factors for a given value. Your original code had the potential of storing data past the end of the array and causing a "smashing" error.
The value of "1" is usually left out of the list of factors for a number, but was left in as the initial result value. This might be left out of the completed code.
An additional entry field was added to allow for user entry to be tested to give the code some flexibility in testing numbers.
A test was also added to see if the entered number is itself a prime number, which would only have factors of "1" and itself.
Following is some sample terminal output testing out your original value of "60" along with some other values.
#Dev:~/C_Programs/Console/Factors/bin/Release$ ./Factors
Enter a number to evaluate for factors: 60
Factors for 60
1 2 2 3 5
#Dev:~/C_Programs/Console/Factors/bin/Release$ ./Factors
Enter a number to evaluate for factors: 63
Factors for 63
1 3 3 7
#Dev:~/C_Programs/Console/Factors/bin/Release$ ./Factors
Enter a number to evaluate for factors: 29
This number is a prime number and has no factors other than one and itself
Give that a try to see if it meets the spirit of your project.

C Programming Occurrences of an Integers in an Text File

Problem: Write a program that reads all integers that are in the range
of 0 to 100, inclusive from an input file named: a.txt and counts how
many occurrences of each are in the file. After all input has been
processed, display all the values with the number of occurrences that
were in are in the input file.
Note: The program ignores any number less than 0 or greater than 100.
Note: Do not display zero if a number is not in the file. Hints: An
array of size 101 is good enough. A number in the file plays the role
of an index.
For example: Suppose the content of the file: a.txt is as follows:
99 2 99
3
-12 80 12 33
3 99 100 1234 84
The display output is:
2 has occurred: 1 times,
3 has occurred: 2 times,
12 has occurred: 1 times,
33 has occurred: 1 times,
80 has occurred: 1 times,
84 has occurred: 1 times,
99 has occurred: 3 times,
100 has occurred: 1 times
Here is the code that I have right now:
#include <stdio.h>
#include <stdlib.h>
int main(){
FILE *inFile;
int count = 1, num[101];
inFile = fopen("a.txt", "r");
for(int i = 1; i <= 100; i++) {
fscanf(inFile, "%d", &num[i]);
}
for(int i = 1; i <= 100; i++){
if(num[i] == i) {
printf("%i has occurred: %d times\n", i, count);
count++;
}
}
fclose(inFile);
}
Output:
2 has occured: 1 times
Hello, I'm trying to do this assignment for my C Programming class due Sunday at midnight, but I'm having trouble trying to print all of the numbers from the array with their occurrences. In my code, I first declared int count to increase the number of occurrences if the number is found more than once in the text file and created an array size of 101. Then, I used a for loop to read the text file and store all the numbers from 1-100 into the array. The second for loop, followed by an if statement is to compare the numbers from the array. Even though this is a test program, we are supposed to be able to do this with all data values. Hope this is a good enough explanation, thank you.
You are close.
Instead of reading each value into num, You want to use your num array to keep the count of each number seen in the file.
int main() {
FILE* inFile;
int value = 0;
int result = 0;
int num[101] = { 0 }; // zero-init this array
inFile = fopen("a.txt", "r");
if (inFile == NULL) {
printf("unable to open file\n");
return -1;
}
result = fscanf(inFile, "%d", &value);
while (result == 1) {
printf("Just read: %d\n", value);
if ((value >= 0) && (value <= 100)) {
num[value] = num[value] + 1; // num[value]++
}
result = fscanf(inFile, "%d", &value);
}
for (int i = 0; i <= 100; i++) {
if (num[i] > 0) {
printf("%i has occurred: %d times\n", i, num[i]);
}
}
fclose(inFile);
}
In addition to the good answer by #selbie, from my answer to your earlier question How do I get the counter to work..., you can apply the same principals to filling a Frequency Array here. In this case you simply use n as an index instead of a counter.
For example with your index n and your array num declared (and initialized all zero), you would simply read all integers in the file and check if the value n was between 0 <= n <= 100 and if so , update the value at the index n in your num array by one, e.g. num[n]++;. You could do it like:
int n = 0; /* index */
int num[NELEM] = {0}; /* array */
...
while (fscanf(myFile, "%d", &n) == 1) /* read each int */
if (0 <= n && n <= 100) /* if 0 <= n <= 100 */
num[n]++; /* increment value at index */
Then for your output, you just handle your special check on num[0] to determine whether to output that index, and then loop from 1-NELEM outputting the frequency of occurrence of each value, e.g.
if (num[0]) /* check if 0 found, output if so */
printf ("num[%3d] occurred %3d times\n", 0, num[0]);
for (int i = 1; i < NELEM; i++) /* output counts for 1-100 */
printf ("num[%3d] occurred %3d times\n", i, num[i]);
The complete example could be:
#include <stdio.h>
#define NELEM 101 /* if you need a constant, #define one (or more) */
int main(int argc, char **argv) {
int n = 0; /* index */
int num[NELEM] = {0}; /* array */
/* read filename from 1st argument (stdin by default) */
FILE *myFile = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!myFile) { /* validate myfile is open for reading */
perror ("fopen-myfile");
return 1;
}
while (fscanf(myFile, "%d", &n) == 1) /* read each int */
if (0 <= n && n <= 100) /* if 0 <= n <= 100 */
num[n]++; /* increment value at index */
if (myFile != stdin) /* close file if not stdin */
fclose (myFile);
if (num[0]) /* check if 0 found, output if so */
printf ("num[%3d] occurred %3d times\n", 0, num[0]);
for (int i = 1; i < NELEM; i++) /* output counts for 1-100 */
printf ("num[%3d] occurred %3d times\n", i, num[i]);
}
Example Use/Output
With 500 random integers in a file, you would get output similar to:
$ ./bin/fscanffreq dat/500_rand_0-100.txt
num[ 0] occurred 3 times
num[ 1] occurred 8 times
num[ 2] occurred 7 times
num[ 3] occurred 2 times
num[ 4] occurred 1 times
num[ 5] occurred 4 times
num[ 6] occurred 3 times
num[ 7] occurred 5 times
num[ 8] occurred 6 times
num[ 9] occurred 4 times
num[ 10] occurred 6 times
...
num[ 95] occurred 6 times
num[ 96] occurred 4 times
num[ 97] occurred 6 times
num[ 98] occurred 2 times
num[ 99] occurred 5 times
num[100] occurred 6 times
(note: if num[0] was 0, it would not be displayed)
Look things over and let me know if you have further questions.

I want to get the inverse of an array while the user input the value

I create a program that get the input of array element size of 10. Everything getting will with the sum of even and odd number. but when it comes to the inverse it didn't work.
i created two arrays where the first getting the value from the user and second copying the element starting from end of the first array..
#include <stdio.h>
int main (){
int array[10] , i , odd =0 , even =0;
int array1[10],b;
for (i=0 ; i < 10 ; i ++){
printf("Insert number %d: ",i);
scanf("%d",&array[i]);
}
for (i=0; i < 10 ; i++){
if ( array[i] % 2 == 0){
even = even + array[i];
}
else
odd = odd + array[i];
}
printf("\n The Sum of Even Numbers in this Array = %d ", even);
printf("\n The Sum of Odd Numbers in this Array = %d ", odd);
for ( i = 10 , b =0; i>0; i-- , b++)
{
array1[b] = array[i];
}
printf("\nReverse Order:\n");
for ( b = 0 ; b< 10;b++ )
{
printf(" %d",array[b]);
}
return 0;
}
The input will be: 2 3 5 4 6 12 3 7 4 9
What I expect the out put for the reverse is: 9 4 7 3 12 6 4 5 3 2
But it gave me same value as : 2 3 5 4 6 12 3 7 4 9 .
Any Idea for how doing this reverse.?
In addition to the answer by #Yunnosch that identifies the problems in your current implementation, you can refactor (rearrange) your code to sum even and odd and reverse array into array1 in a single loop. The only other loop you need is the loop to iterate over array1 outputting the reversed array.
With a bit of re-arranging, you could do something similar to:
#include <stdio.h>
int main (void) {
int array[] = { 2, 3, 5, 4, 6, 12, 3, 7, 4, 9 }, /* array */
array1[sizeof array/sizeof *array], /* array1 */
even = 0, odd = 0; /* even/odd */
size_t n = sizeof array/sizeof *array; /* no. elem in array */
for (size_t i = 0; i < n; i++) { /* loop over each element in array */
array1[i] = array[n - i - 1]; /* reverse into array1 */
if (array[i] & 1) /* check if odd (bit-0 == 1) */
odd += array[i]; /* add value to odd */
else /* even */
even += array[i]; /* add value to even */
}
/* output results */
printf ("even sum: %d\nodd sum : %d\n\nreversed: ", even, odd);
for (size_t i = 0; i < n; i++)
printf (" %d", array1[i]);
putchar ('\n');
}
(note: you can either use if (array[i] % 2) or if (array[i] & 1) to test whether the element is odd or even. Anding with 1 simply checks whether bit-0 is 1, if it is, it's an odd number. Modern compilers will optimize to remove the division inherent to modulo, so whichever you prefer should pose no penalty)
Example Use/Output
$ ./bin/revarr
even sum: 28
odd sum : 27
reversed: 9 4 7 3 12 6 4 5 3 2
Look things over and let me know if you have questions.
You are outputting the array which you never tried to inverse.
printf(" %d",array[b]);
should be
printf(" %d",array1[b]);
Aside, the input by David C. Rankin:
Also for ( i = 10 ... and array1[b] = array[i]; assigns from beyond the end of array. It should e.g. better be
for ( i = 10 , b =0; i>0; i-- , b++)
{
array1[b] = array[i-1];
}

Is there a way to read a number from a file that is bigger than long long int?

I'm trying to read a file that contains a sequence of numbers
Example:
43917778700000762841090006653470575088202000
43919478100000762851090006653540575088203000
43919765400000815661090006653620575088204000
My code:
typedef struct rArc{
long long int code;
}data;
int main(){
long long int code,i=0;
FILE *arc;
data *v;
v = NULL;
arc = fopen("CODES.txt","r");
while(fscanf(arc,"%lld",&code) != EOF){
v = (data *)realloc(v,sizeof(data) * (i + 1));
v[i].code = code;
i++;
}
for(int j=0;j<i;j++){
printf("%lld\n",v[j].code);
}
}
But because the number is too big, it outputs some wierd random numbers.
I was thinking in taking the last digit with modulo and adding into a vector, but because I cant even read it there's no way to do it the way i'm trying
Update
I've managed to read the file by changing the struct and main code variables to char code[45], I'm still working on how to do the maths but it was a start i guess
while(fscanf(arc,"%45s\n",&code) != EOF){
v = (data *)realloc(v,sizeof(data) * (i + 1));
v[i].code = code;
i++;
}
for(int j=0;j<i;j++){
printf("%45s\n",v[j].code);
}
}
Even after 3 iterations of clarification in the comments, I'm still not entirely clear on your algorithm, but I believe I understand it enough to help. From your responses to my comments, I understand you want to replace text at the 10th, 21st & 32nd characters with values arrived at by:
summing all characters to the left of the position,
the sum is produced by multiplying every other character by 2 starting by multiplying the 1st character by 2, the second by 1, the third by 2 and continuing with the 1-2-1-2-1... pattern of multiplying each digit until the wanted index is reached, and finally
if any multiple by 2 exceeds 10, then you increase the sum by the product modulo 10 (e.g. sum = sum + (digit * 2-1-multiple) % 10) (or you may want to mod to total accumulated sum by 10 before each replacement at the 10th, 21st and 32nd characters -- that part is still unclear)
That being so, you can approach the problem by reading each string of digits into a fixed buffer. (do not skimp on buffer size). Once the line is read, iterate over the characters (validating each is a digit) keeping a running sum according to the pattern above until you reach the desired index. At the required index, replace the characters in the original string with the sum at that point, and continue until the final index and replacement at the 32nd character has been made.
To do that you can start by declaring your buffer to hold the file and opening the file (and validating it is open). You can take the filename to open as the 1st argument to your program or read from stdin if no argument is provided, e.g.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 512 /* constant for line-buffer size */
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* buffer to read each line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
Next you will want to begin your read loop and declare the variables required to locate the desired positions to insert the sums, a way to keep track of your 2-1-2-1... multiplier sequence and your running sum. While not required here (you will only access through the 32nd character, it is a good idea to trim the trailing '\n' included in your buffer by fgets (or POSIX getline) as part of your validation that all characters fit in your buffer, e.g.
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
int const pos[] = { 10, 21, 32 }, /* indexes to replace */
npos = sizeof pos / sizeof *pos; /* no. of indexes */
int ndx = 0, /* buffer index */
*pdx = (int *)pos, /* pointer to pos */
mult = 2; /* 2-1-2-... multiplier */
size_t len = strlen (buf); /* length of string */
unsigned sum = 0; /* sum of digits */
if (len && buf[len - 1] == '\n') /* check for trailing '\n' */
buf[--len] = 0; /* overwrite with nul-character */
else if (len == MAXC - 1) { /* otherwise string too long */
fprintf (stderr, "error: line too long.\n");
return 1;
}
printf ("original: %s\n", buf); /* output the original string */
Once you have read your line and validated it fit, etc.. you need to loop over each character in the string performing your sum increment and replacement where required. You should also validate that each character read is a digit and not some stray alpha-character, or otherwise. By using a simple preprocessor #define passed as part of your compile string, you can provide optional debug output to help you sort out any problems with your algorithm, e.g.
for (; buf[ndx]; ndx++) { /* iterate over each character */
if (!isdigit (buf[ndx])) { /* validate character is digit */
fprintf (stderr, "error: non-digit '%c'.\n", buf[ndx]);
return 1;
}
sum += ((buf[ndx] - '0') * mult) % 10; /* increment sum by % 10 */
if (ndx + 1 == *pdx) { /* check if ndx+1 is position */
int ndigit = 0; /* no. of digits in sum */
char tmp[MAXC] = ""; /* tmp buffer for sum as string */
ndigit = sprintf (tmp, "%u", sum); /* write sum to tmp */
#ifdef DEBUG /* debug output */
printf ("ndx+1: %2d, sum: %3u, ndigits: %d\n",
ndx+1, sum, ndigit);
#endif
if (ndigit) /* validate characters written to tmp */
memcpy (&buf[ndx], tmp, ndigit); /* copy to buf */
pdx++; /* increment pos array index */
if (*pdx == npos) /* check if past last pos index */
break;
}
mult = (mult & 1) ? 2 : 1; /* toggle mult 2-1-2-1... */
}
printf ("revised : %s\n\n", buf); /* output updated number in buf */
}
Putting it altogether, and adding the close of open file, you could do something like the following:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 512 /* constant for line-buffer size */
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* buffer to read each line */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
int const pos[] = { 10, 21, 32 }, /* indexes to replace */
npos = sizeof pos / sizeof *pos; /* no. of indexes */
int ndx = 0, /* buffer index */
*pdx = (int *)pos, /* pointer to pos */
mult = 2; /* 2-1-2-... multiplier */
size_t len = strlen (buf); /* length of string */
unsigned sum = 0; /* sum of digits */
if (len && buf[len - 1] == '\n') /* check for trailing '\n' */
buf[--len] = 0; /* overwrite with nul-character */
else if (len == MAXC - 1) { /* otherwise string too long */
fprintf (stderr, "error: line too long.\n");
return 1;
}
printf ("original: %s\n", buf); /* output the original string */
for (; buf[ndx]; ndx++) { /* iterate over each character */
if (!isdigit (buf[ndx])) { /* validate character is digit */
fprintf (stderr, "error: non-digit '%c'.\n", buf[ndx]);
return 1;
}
sum += ((buf[ndx] - '0') * mult) % 10; /* increment sum by % 10 */
if (ndx + 1 == *pdx) { /* check if ndx+1 is position */
int ndigit = 0; /* no. of digits in sum */
char tmp[MAXC] = ""; /* tmp buffer for sum as string */
ndigit = sprintf (tmp, "%u", sum); /* write sum to tmp */
#ifdef DEBUG /* debug output */
printf ("ndx+1: %2d, sum: %3u, ndigits: %d\n",
ndx+1, sum, ndigit);
#endif
if (ndigit) /* validate characters written to tmp */
memcpy (&buf[ndx], tmp, ndigit); /* copy to buf */
pdx++; /* increment pos array index */
if (*pdx == npos) /* check if past last pos index */
break;
}
mult = (mult & 1) ? 2 : 1; /* toggle mult 2-1-2-1... */
}
printf ("revised : %s\n\n", buf); /* output updated number in buf */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(note: the algorithm above includes the replaced values as part of the sum used at the next replacement index. i.e. your replacement at the 10th character is 52 for the first line. The digits 5 and 2 are used as part of the sum inserted at the 21st character)
Compile With DEBUG Defined (-DDEBUG)
$ gcc -Wall -Wextra -pedantic -std=c11 -Ofast -DDEBUG \
-o bin/str_fill_sum_dbg str_fill_sum.c
(note: the '\' line-continuation is used above simply to keep the compile string from scrolling beyond the right margin of the web-page here. Also, I put all compiled executables in a bin subdirectory to keep my source directory clean, you can omit the bin/ portion of the executable name)
Example DEBUG Use/Output
The debug output shows the index, the current sum at that index which is inserted, and the number of characters replaced:
$ ./bin/str_fill_sum_dbg <dat/sumdigits.txt
original: 34194716400003108771090006638210572088201000
ndx+1: 10, sum: 52, ndigits: 2
ndx+1: 21, sum: 79, ndigits: 2
ndx+1: 32, sum: 109, ndigits: 3
revised : 34194716452003108771790006638211092088201000
original: 34193716400000921121090006638390572088201000
ndx+1: 10, sum: 50, ndigits: 2
ndx+1: 21, sum: 68, ndigits: 2
ndx+1: 32, sum: 104, ndigits: 3
revised : 34193716450000921121680006638391042088201000
original: 34191718400000607281090006638470572088201000
ndx+1: 10, sum: 48, ndigits: 2
ndx+1: 21, sum: 69, ndigits: 2
ndx+1: 32, sum: 103, ndigits: 3
revised : 34191718448000607281690006638471032088201000
original: 34195718400000550361090006638540572088201000
ndx+1: 10, sum: 46, ndigits: 2
ndx+1: 21, sum: 59, ndigits: 2
ndx+1: 32, sum: 98, ndigits: 2
revised : 34195718446000550361590006638549872088201000
original: 34192719900000550361090006638620572088201000
ndx+1: 10, sum: 51, ndigits: 2
ndx+1: 21, sum: 64, ndigits: 2
ndx+1: 32, sum: 95, ndigits: 2
revised : 34192719951000550361640006638629572088201000
original: 34198721400000550361090006638700572088201000
ndx+1: 10, sum: 47, ndigits: 2
ndx+1: 21, sum: 62, ndigits: 2
ndx+1: 32, sum: 88, ndigits: 2
revised : 34198721447000550361620006638708872088201000
Compile Normally
Simply drop the definition of the DEBUG define to omit the debug output, and out the executable in a separate filename if you like so both are available:
$ gcc -Wall -Wextra -pedantic -std=c11 -Ofast \
-o bin/str_fill_sum str_fill_sum.c
Example Use/Output
$ ./bin/str_fill_sum <dat/sumdigits.txt
original: 34194716400003108771090006638210572088201000
revised : 34194716452003108771790006638211092088201000
original: 34193716400000921121090006638390572088201000
revised : 34193716450000921121680006638391042088201000
original: 34191718400000607281090006638470572088201000
revised : 34191718448000607281690006638471032088201000
original: 34195718400000550361090006638540572088201000
revised : 34195718446000550361590006638549872088201000
original: 34192719900000550361090006638620572088201000
revised : 34192719951000550361640006638629572088201000
original: 34198721400000550361090006638700572088201000
revised : 34198721447000550361620006638708872088201000
Look things over and let me know if you have further questions. If you algorithm is different than shown above, you should be able to easily incorporate any needed changes.
If modulo Of Total Before Insertion
If instead of taking modulo 10 of the result of each digit multiplied by 2, you instead want to just take the modulo of the sum before insertion, you could replace your character iteration loop with the following:
for (; buf[ndx]; ndx++) { /* iterate over each character */
if (!isdigit (buf[ndx])) { /* validate character is digit */
fprintf (stderr, "error: non-digit '%c'.\n", buf[ndx]);
return 1;
}
sum += ((buf[ndx] - '0') * mult); /* increment by digit*mult */
if (ndx + 1 == *pdx) { /* check if ndx+1 is position */
int replace = sum % 10;
#ifdef DEBUG /* debug output */
printf ("ndx+1: %2d, sum: %3u, replace: %d\n",
ndx+1, sum, replace);
#endif
buf[ndx] = replace + '0'; /* replace char at buf[ndx] */
pdx++; /* increment pos array index */
if (*pdx == npos) /* check if past last pos index */
break;
}
mult = (mult & 1) ? 2 : 1; /* toggle mult 2-1-2-1... */
}
Example DEBUG Use/Output
In that case the replacements of a single character would be as follows:
$ ./bin/str_fill_sum_dbg2 <dat/sumdigits.txt
original: 34194716400003108771090006638210572088201000
ndx+1: 10, sum: 52, replace: 2
ndx+1: 21, sum: 95, replace: 5
ndx+1: 32, sum: 145, replace: 5
revised : 34194716420003108771590006638215572088201000
original: 34193716400000921121090006638390572088201000
ndx+1: 10, sum: 50, replace: 0
ndx+1: 21, sum: 78, replace: 8
ndx+1: 32, sum: 145, replace: 5
revised : 34193716400000921121890006638395572088201000
original: 34191718400000607281090006638470572088201000
ndx+1: 10, sum: 48, replace: 8
ndx+1: 21, sum: 93, replace: 3
ndx+1: 32, sum: 157, replace: 7
revised : 34191718480000607281390006638477572088201000
original: 34195718400000550361090006638540572088201000
ndx+1: 10, sum: 56, replace: 6
ndx+1: 21, sum: 87, replace: 7
ndx+1: 32, sum: 146, replace: 6
revised : 34195718460000550361790006638546572088201000
original: 34192719900000550361090006638620572088201000
ndx+1: 10, sum: 61, replace: 1
ndx+1: 21, sum: 92, replace: 2
ndx+1: 32, sum: 148, replace: 8
revised : 34192719910000550361290006638628572088201000
original: 34198721400000550361090006638700572088201000
ndx+1: 10, sum: 57, replace: 7
ndx+1: 21, sum: 88, replace: 8
ndx+1: 32, sum: 141, replace: 1
revised : 34198721470000550361890006638701572088201000
//This Program will solve your problem. If you have any doubt ask me
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int sum10th = 0 , sum21st = 0 , sum32nd = 0; //to store the sum of 10th 21st 32nd
FILE *stream;
char *line = NULL;
size_t len = 0;
ssize_t nread;
stream = fopen("number.txt","r");
while ((nread = getline(&line, &len, stream)) != -1)
{
if( nread >= 10)
{
sum10th += line[10 - 1] - 48 ;
if( nread >= 21)
{
sum21st += line[21 - 1] - 48;
if( nread >= 32)
sum32nd += line[32 - 1] - 48;
}
}
strcpy(line," "); //empty line
}
free(line);
fclose(stream);
printf("Sum at 10th 21st 32th is %d %d %d\n",sum10th , sum21st, sum32nd);
return 0;
}
/*
number.txt
46546546546548614684654654651666
65465455546868464654684797987987
46865465465465465465654686468468
*/
/*
ouput
Sum at 10th 21st 32th is 14 18 21
*/

How can I stop before reading the next line?

I have to read a file like this:
0 -> 1:50 2:30 3:10
1 ->
2 -> 0:10 3:20
3 -> 1:20 2:10 3:30
Here's my code:
graphs = fopen(argv[2],"r");
if(graphs==NULL){
printf("File hostgraphs not found\n");
exit(EXIT_FAILURE);
}
while((err=fscanf(graphs,"%d ->",&num))==1){
row=num;
while((err1=fscanf(graphs," %d:%d ",&column,&visits))==2){
hostgraphs[row*n+column]=visits;
}
if(err1!=2)
break;
if(err==0){
printf("Hostgraph out of bounds\n");
exit(EXIT_FAILURE);
}
}
for(i=0;i<n;i++){
for(j=0;j<n;j++){
printf("%d ", hostgraphs[i*n+j]);
}
printf("\n");
}
It gives me the following result:
0 50 30 10
0 0 0 0
0 0 0 0
0 0 0 0
The expected output must be:
0 50 30 10
0 0 0 0
10 0 0 20
0 20 10 30
Can anyone help me please?
You can do this by reading the file, one line at a time, using getline, and then applying the same logic, as you are doing, on each line separately.
In the following code, I first get a single line from the file in buffer using getline method. Then I process that line as a FILE pointer fbuffer using fmemopen method, which is used to read a string like a file:
graphs = fopen(argv[2],"r");
if(graphs==NULL)
{
printf("File hostgraphs not found\n");
exit(EXIT_FAILURE);
}
char *buffer = NULL;
size_t len = 0;
while(getline(&buffer, &len, graphs) != -1)
{
FILE * fbuffer = fmemopen(buffer, len, "r");
while((err=fscanf(fbuffer,"%d -> ",&num))==1)
{
row=num;
while((err1=fscanf(fbuffer," %d:%d ",&column,&visits))==2)
{
hostgraphs[row*n+column]=visits;
}
if(err1!=2)
{
break;
}
if(err==0)
{
printf("Hostgraph out of bounds\n");
exit(EXIT_FAILURE);
}
}
free(buffer);
buffer = NULL;
len = 0;
}
for(i=0;i<n;i++)
{
for(j=0;j<n;j++)
{
printf("%d ", hostgraphs[i*n+j]);
}
printf("\n");
}
Note: I have just added code to make your existing code work, without making any changes to your code.
The fix is simple: remove the trailing space in the fscanf format string in
fscanf(graphs," %d:%d ",&column,&visits)
It should be simplified as
fscanf(graphs,"%d:%d", &column, &visits)
fscanf ignores the newline then after read first row, it moves to next row and read 5 and results 1 in err1. That is the root-cause. You can add fseek to move back and start new sequence as bellow code-snip or simply change the algorithm to another effective one.
graphs = fopen(argv[2],"r");
if (!graphs) {
printf("File hostgraphs not found<%s>\n", argv[1]);
exit(EXIT_FAILURE);
}
while((err = fscanf(graphs, "%d ->", &num)) == 1) {
pos = ftell(graphs); // save position
while((err1 = fscanf(graphs, " %d:%d", &column, &visits)) == 2 ) {
hostgraphs[num * n + column] = visits;
pos = ftell(graphs); // save position
}
// seek back to previous one and start new sequence
fseek ( graphs , pos, SEEK_SET );
}
for(i = 0; i< n; i++){
for(j = 0; j < n; j++){
printf("%d ", hostgraphs[i * n + j]);
}
printf("\n");
}
You were close, but you needed a way of locating each '\n' without reading the next row value from the file. That is very difficult to do with fscanf where you have varying number of elements in each row in your input file.
Another approach is to use fgets to read the entire line, and then separate the initial "row -> " prefix from the data values. If you move the values into a separate values buffer (say vbuf), you can then repeatedly loop through the buffer by finding the space, advancing to the next digit and then using sscanf to split the column and visits.
(you actually don't even need to split the values in vbuf, you can simply use a pointer to advance past the "row -> " text working with the entire line.)
The following example puts those pieces together and parses the values into the correct positions in hostgraphs. Look it over and let me know if you have questions:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { COLS = 4, MAXH = 16, MAXC = 64 };
int main (int argc, char **argv) {
char buf[MAXC] = "", vbuf[MAXC] = "";
char *p = NULL, *rfmt = "%d -> %[^\n]";
int row, column, visits, hostgraphs[MAXH] = {0};
size_t i, j, n = 0;
FILE *graphs = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!graphs) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, graphs))
{
*vbuf = 0; /* initialize vbuf, split %d:%d pars into vbuf */
if (sscanf (buf, rfmt, &row, vbuf) < 1)
break;
if (!*vbuf) { /* check vbuf contians values */
n++;
continue;
}
/* read 1st pair */
if (sscanf (vbuf, "%d:%d", &column, &visits) != 2) {
fprintf (stderr, "error: invalid line format\n");
exit (EXIT_FAILURE);
}
hostgraphs[row*COLS+column] = visits;
p = vbuf; /* assign p, parse remaining pairs */
while ((p = strchr (p, ' '))) /* find space */
{
while (*p < '0' || '9' < *p) /* find digit */
p++;
if (sscanf (p, "%d:%d", &column, &visits) == 2)
hostgraphs[row*COLS+column] = visits;
}
n++; /* advance row count */
}
for (i = 0; i < n; i++) { /* output values */
printf (" row[%2zu] : ", i);
for(j = 0; j < COLS; j++) {
printf (" %2d", hostgraphs[i * COLS + j]);
}
printf ("\n");
}
if (graphs != stdin)
fclose (graphs);
return 0;
}
Input File
$ cat ../dat/hostgraph.txt
0 -> 1:50 2:30 3:10
1 ->
2 -> 0:10 3:20
3 -> 1:20 2:10 3:30
Output
$ ./bin/hostgraph < ../dat/hostgraph.txt
row[ 0] : 0 50 30 10
row[ 1] : 0 0 0 0
row[ 2] : 10 0 0 20
row[ 3] : 0 20 10 30

Resources