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
Related
I'm trying to write a program as an exercise that takes some values from a file sorts them into two variables named studentsPassed and studentsFailed, then prints the number of students that have passed and the number of students that have failed aswell as the module code and the number of students.
Here is the .txt file:
101 20
65 72 23 59 80 75 55 88 92 77 44 57 73 31 48 59 71 48 66 59
101 is the module code, and 20 is the number of students in the class. The 20 numbers in the row below are the marks each one of those 20 students scored (e.g. Student 1 scored 65, Student 2 scored 72, etc.)
Here is my code:
#include <stdio.h>
FILE *fp;
int main(){
//Open the file and assign its address/disk location to file pointer
fp = fopen("marks.txt", "r");
//Variables
int moduleCode, numStudents;
const int size = 20;
int studentMark;
int studentsPassed;
int studentsFailed;
//Scan in the first line for the module code and the number of students
fscanf(fp, "%d %d", &moduleCode, &numStudents);
//Scan in the student marks and loop through them
for (int i = 0; i < size; i++)
if (fscanf(fp, "%d", &studentMark) < 40){
int studentsFailed = studentsFailed + 1;
}
else if (fscanf(fp, "%d", &studentMark) >= 40){
int studentsPassed = studentsPassed + 1;
}
else{
printf("Error: Number of marks exceeds cap!");
return 0;
}
//Print the results
printf("%d %d\n", moduleCode, numStudents);
printf("Number of students passed: %d\n", studentsPassed);
printf("Number of students failed: %d\n", studentsFailed);
return 0;
}
What the program should do is read the module code and the number of students and print them on the first line (which is successfully does), then the program should loop through each of the 20 numbers and sort them into different variables based on if they are higher or lower than 40, this is the part that I am struggling on, as the program executes, but it will print out a random large number for studentsPassed, and "1" for studentsFailed every time I run it.
What am I doing wrong? I feel like I am missing something to do with looping through each of the numbers but I'm not sure how to correct it.
Note: This is what I initially tried (which also didn't work) before reading another answer on this website to get what my current code is.
//Scan in the student marks and loop through them
fscanf(fp, "%d", &studentMark);
for (int i = 0; i < size; i++)
if (studentMark < 40){
int studentsFailed = studentsFailed + 1;
}
else if (studentMark >= 40){
int studentsPassed = studentsPassed + 1;
}
Your second version is almost correct, except that the call to fscanf() needs to be inside the loop.
Also, you shouldn't re-declare studentsFailed and studentsPassed in the if blocks. Just assign to the variables that were declared earlier.
for (int i = 0; i < size; i++)
fscanf(fp, "%d", &studentMark);
if (studentMark < 40){
studentsFailed++;
}
else{
studentsPassed++;
}
}
You also shouldn't use else if when the second condition is the opposite of the first one. Just use else.
The value that *scanf returns is the number of conversion specifiers that were successfully matched. It is unrelated to the values that it reads from the input stream.
You should get out of the habit of relying on the input stream to provide the number of expected data points; it is fragile and usually not necessary. Instead, just read all of the data until you reach the end of the input stream. In your case, you can do something like:
#include <stdio.h>
int
main(int argc, char **argv)
{
int moduleCode, numStudents, studentsPassed = 0, studentsFailed = 0;
int actualNumStudents = 0;
int score;
FILE *fp = argc > 1 ? fopen(argv[1], "r") : stdin;
if( fp == NULL ){
perror(argv[1]);
return 1;
}
if( fscanf(fp, "%d%d", &moduleCode, &numStudents) != 2 ){
fprintf(stderr, "invalid input\n");
return 1;
}
while( fscanf(fp, "%d", &score) == 1 ){
actualNumStudents += 1;
*( score < 40 ? &studentsFailed : &studentsPassed ) += 1;
}
if( ! feof(fp) ){
fputs(ferror(fp) ? "read error\n" : "invalid input\n", stderr);
return 1;
}
if( actualNumStudents != numStudents ){
fprintf(stderr, "Warning: the declared number of students does "
"not match the actual number of students\n");
}
printf("%d %d\n", moduleCode, actualNumStudents);
printf("Number of students passed: %d\n", studentsPassed);
printf("Number of students failed: %d\n", studentsFailed);
}
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;
}
I have a textfile of numbers written in words, with spaces between like..
zero three five two one .. etc there are 3018 words in total.
This is my code:
#include <stdio.h>
int main(void)
{
int i = 0;
int d = 0;
int j = 0;
char array[9054][5];
char hi[9054];
FILE *in_file;
in_file = fopen("message.txt", "r");
while (!feof(in_file))
{
fscanf(in_file, "%s", array[i]);
i++;
}
printf(array[9049]);
while (1);
return 0;
}
so the 9049th worth in my textfile is the number three.. but when I run this script, it prints "threethreezero"instead?? i thought the fscanf ignored whitespace (spaces) so why does accept another three and zero into this string?
OP figured things out with the help of comments, so here is a cumulative fix.
#include <stdio.h>
int main(void)
{
int i = 0;
int d = 0;
int j = 0;
// Make room for the null character
char array[9054][5+1];
char hi[9054];
FILE *in_file;
in_file = fopen("message.txt", "r");
//check `fscanf()`'s return value rather than using feof()
// Limit input put with 5
while (fscanf(in_file, "%5s", array[i]) == 1);
i++;
}
// Check that code read enough input
if (i >= 9049) {
// Do not use `printf()` on uncontrolled strings that may contain %
fputs(array[9049], stdout);
} else {
puts("Oops");
}
while (1);
return 0;
}
I am trying to take all the integers from the file file.txt and place them in a dynamically allocated array. However the file could also contain other characters, and these should not be put in the array.
The file contains the following:
2 -34 56 - 23423424
12example-34en+56ge-tal345
int* getIntegers(char* filename, int* pn)
{
FILE* fileInput = fopen(filename, "r");
int* temp = (int*)malloc( 100*sizeof(int));
int counter = 0;
int c= fgetc(fileInput);
while(c != EOF){
counter ++;
printf("%d;\t%d\n", counter, c);fflush(stdout);
temp[counter++] = c;
}
*pn = counter;
return (temp);
}
int main(void)
{
int n;
int* a = getIntegers("file.txt", &n);
if (a != NULL){
puts("numbers found:");
for (int i = 0;i < n; i++){
printf("%d ",a[i]);
}
free(a);
}
putchar('\n');
while(getchar()== '\n');
return (EXIT_SUCCESS);
}
when I run this the following output is returned:
Output:
1; 49
3; 49
5; 49
7; 49
9; 49
11; 49
13; 49
15; 49
17; 49
19; 49
21; 49
While the correct output should have been
found numbers:
12 -34 56 23423424 12 -34 56 345
There are many things wrong with that program.
You leak your file object. You should close it with fclose() when done.
If there are more than 100 numbers in your input, you will overflow your buffer, trash your stack, and do Bad Things to your program.
You increment your counter twice at every loop iteration, so you'll skip every second entry in your output array.
You never read another byte from the input, so you're just going to keep processing the same byte over and over in an infinite loop until your buffer overflow causes your program to crash.
You never convert the digits that you read from your input file into an integer; instead, you just take the character code. 49 is the ASCII/UTF-8 code for '1', which appears to be the first character in your input.
try this
#include <stdlib.h>
#include <stdio.h>
int* getIntegers(char* filename, int* pn)
{
int* temp = (int*)malloc( 100*sizeof(int));
int counter = 0;
int result;
int c;
FILE* fileInput = fopen(filename, "r");
if ( fileInput == NULL) {
return temp; // return if file open fails
}
while( 1) {
result = fscanf (fileInput, "%d", &c);//try to read an int
if ( result == 1) { // read successful
temp[counter] = c; //save int to array
counter++;
printf("%d;\t%d\n", counter, c);
}
else { // read not successful
fscanf ( fileInput, "%*[^-+0-9]"); //scan for anything not a -, + or digit
}
if ( counter > 98) { // dont exceed array
break;
}
if ( feof( fileInput)) { // check if at end of file
break;
}
}
fclose ( fileInput); // close the file
*pn = counter;
return (temp);
}
int main(void)
{
int n;
int i;
int* a = getIntegers("file.txt", &n);
if (a != NULL){
printf("numbers found:");
for (i = 0;i < n; i++){
printf("%d ",a[i]);
}
free(a);
}
putchar('\n');
return (EXIT_SUCCESS);
}
Many loop holes in your code.You need to first fix it.
int c= fgetc(fileInput);
while(c != EOF)
{
counter ++;
printf("%d;\t%d\n", counter, c);fflush(stdout);
temp[counter++] = c;
}
This code make me crazy. What you gain by reading only one character from file and run while loop? Give chance to read another byte.
Must check the return value fopen function. what if files not present OR error in opening? Your program gone wild. So ensure
FILE* fileInput = fopen(filename, "r");
if(fileInput==NULL ){
printf("Error to open file\n");
return
}
Also you incremented counter two times in loop first one is counter ++; and second one is temp[counter++] = c; this is wrong to manage array index.
Also most important thing is every open file must be close.
I have written a small program which takes input of a file such as:
13,22,13,14,31,22, 3, 1,12,10
11, 4,23, 7, 5, 1, 9,33,11,10
40,19,17,23, 2,43,35,21, 4,34
30,25,16,12,11, 9,87,45, 3, 1
1,2,3,4,5,6,7,8,9,10
and outputs the largest sum of numbers on each line that is less than 50.
However if the inputted file has a trailing newline character the loop runs one too many times and hence another line is added to the array with random data. So I'm looking for a better way to do this comparison to avoid this issue. I'm also assuming all lines have 10 integers on at the moment as i cannot think of a better way to do the end of line loop comparison.
#include <stdio.h>
#include <stdlib.h>
void readLineData(int lineNo, int val[][10], FILE *fp);
int findSum(int lineNo, int val[][10], FILE *fp);
int main(int argc, char *argv[]) {
FILE *fp;
int val[5][10];
// Open file.
if ((fp = fopen(argv[1], "r")) == NULL)
{
perror("Cannot open file ");
exit(EXIT_FAILURE);
}
for (int i = 0; !feof(fp); i++) // runs too many times if file ends with '\n'
{
readLineData(i, val, fp);
printf("%d\n", findSum(i, val, fp));
}
fclose(fp);
return EXIT_SUCCESS;
}
void readLineData(int lineNo, int val[][10], FILE *fp) {
char c;
for (int i = 0; i < 10; i++) // assuming line contains 10 integers
{
fscanf(fp, "%d,", &val[lineNo][i]);
}
}
int findSum(int lineNo, int val[][10], FILE *fp) {
int highVal = 0;
int value1 = 0;
int value2 = 0;
for(int i = 0; i < 10; i++) //each letter
{
for(int j = 0; j < 10; j++)// every other letter
{
if((val[lineNo][i] + val[lineNo][j]) > highVal && i != j && (val[lineNo][i] + val[lineNo][j]) <= 50)
{
highVal = val[lineNo][i] + val[lineNo][j];
value1 = val[lineNo][i];
value2 = val[lineNo][j];
}
}
}
printf("Line %d: largest pair is %d and %d, with a total of: ", lineNo+1, value1, value2);
return highVal;
}
any help with those loop comparisons and general notation tips is most welcome.
Thanks
The posted code does not distinguish between two lines that have five integers and (the expected) one line that has 10 integers. Suggest reading in a line at a time, using fgets() and then using sscanf() on the read line to ensure that all the read integers belong to the same line.
Check the return value of input operations. For example, sscanf() (and fscanf()) return the number of assignments made. Only process lines that have the expected 10 integers, which would detect invalid lines including the trailing empty line.
For example:
/* Returns 1 on success and 0 on failure. */
int readLineData(int lineNo, int val[][10], FILE *fp)
{
char line[1024]; /* Arbitrarily large. */
if (fgets(line, sizeof(line), fp))
{
/* %n records position where processing ended. */
int pos;
const int result = sscanf(line,
"%d,%d,%d,%d,%d,%d,%d,%d,%d,%d%n",
&val[lineNo][0],
&val[lineNo][1],
&val[lineNo][2],
&val[lineNo][3],
&val[lineNo][4],
&val[lineNo][5],
&val[lineNo][6],
&val[lineNo][7],
&val[lineNo][8],
&val[lineNo][9],
&pos);
/* 10 integers and full line processed,
except if new-line character present. */
return 10 == result &&
(pos == strlen(line) ||
(pos + 1 == strlen(line) && '\n' == line[pos]));
}
return 0;
}
You could simply consume the newline character yourself:
for (int i = 0; !feof(fp); i++) // runs too many times if file ends with '\n'
{
readLineData(i, val, fp);
printf("%d\n", findSum(i, val, fp));
fscanf(fp, "%*c"); // read a character without storing it in a variable
}
Note that there are undoubtedly better ways that involve reading an entire line at once and simply examining its contents; but this is the easiest way that will fit with what you already have.
you could check if fscanf fails in your readLineData function:
int readLineData(int lineNo, int val[][10], FILE *fp) {
for (int i = 0; i < 10; i++) {// assuming line contains 10 integers
if (fscanf(fp, "%d,", &val[lineNo][i]) != 1) {
return 1;
}
}
return 0;
}