Following is my program. I try to read the file rgb_values.txt
and transfer its data to out.txt
. rgb_values.txt
is very long with about 2.7 million lines, but when I run the program and check out.txt
; it has only 2.5 million lines (2554994 lines to be exact) which means that in this line the program reads to EOF. However, this should not be the case. I don't know what went wrong
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<math.h>
#include <stdio.h>
int count=0;
void getOneByte(FILE *plaintext,unsigned char *pt,int i){
char line[4]={0x0};
fgets(line,sizeof(line),plaintext);
int x1=0x0;
char str[4];
sprintf(str,"%s",line);
sscanf(str,"x ",&x1);
pt[i]=x1;
}
void get16Bytes(FILE *plaintext,unsigned char * pt){
int i=0;
char sh;
for (int i = 0; i <=15; i )
{
getOneByte(plaintext,pt,i);
if((sh=fgetc(plaintext))!=EOF){
ungetc(sh,plaintext);
}
else{
ungetc(sh,plaintext);
break;
}
}
}
void write16Bytes(FILE *ciphertext,unsigned char *ct){
int i;
for(i = 0; i < 16; i ){
if(count == 0){
fprintf(ciphertext,"x",ct[i]);
count ;
}
else if(count == 1){
fprintf(ciphertext," x",ct[i]);
count ;
}
else if(count == 2){
fprintf(ciphertext," x\n",ct[i]);
count = 0;
}
}
}
int main(){
FILE *fp=fopen("rgb_values.txt","r");
FILE *fo=fopen("out.txt","w");
char ch;
unsigned char pt[16];
int i=15;
int cnt=0;
while(1){
memset(pt,'a',sizeof(pt));
get16Bytes(fp,pt);
if((ch=fgetc(fp))!=EOF){
ungetc(ch,fp);
}
else{
break;
}
write16Bytes(fo,pt);
cnt ;
}
fclose(fp);
return 1;
}
Part of my input file:
c1 c1 c1
ff ff ff
ff ff ff
ff ff ff
ff ff ff
fe fe fe
fd fd fd
ff ff ff
fb fb fb
fe fe fe
fe fe fe
ff ff ff
fb fb fb
f1 f1 f1
e9 e9 e9
e6 e6 e6
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e5 e5 e5
e2 e2 e2
e5 e5 e5
e7 e7 e7
e5 e5 e5
e4 e4 e4
e3 e3 e3
e3 e3 e3
e8 e8 e8
de de de
9c 9c 9c
3b 3b 3b
01 01 01
02 02 02
00 00 00
02 02 02
0a 0a 0a
CodePudding user response:
I don't know what went wrong
Coding is not defensive as it does not look for unexpected events.
To learn what went wrong, improve code's error detection.
In other words, don't trust user input - its evil.
Why read only 3 bytes?
The below reads, at most 3 bytes from the file, leaving the remainder of a line for later reading.
char line[4]={0x0};
fgets(line,sizeof(line),plaintext);
It does not read only 3 bytes from a file and toss the remainder of the line.
Check return value of fgets()
// fgets(line,sizeof(line),plaintext);
if (fgets(line,sizeof(line),plaintext) == NULL) {
TBD_alert();
}
Use int
int fgetc()
can return 257 different values. Saving in a char
loses something.
// char ch;
int ch;
// char sh;
int sh;
Lack of error checking
What if sscanf(str,"x ",&x1)
returns 0 (no conversion)?
What if 4 is too small?
Should an extra white-space exist, (space, '\r'
, ...), 4 is simply too small.
char line[4]={0x0};
fgets(line,sizeof(line),plaintext);
Instead read a complete line with fgets()
allowing at least a 2x size buffer over expected size and tolerate extra white-space or a missing '\n'
on the last line. Given a line of "c1 c1 c1\n"
, use a buffer size of (3*3 1)*2.
I suspect many '\r'
lurking about.
Avoid UB. Use matching specifiers
"%x"
matches an unsigned
, not int
.
// int x1=0x0;
unsigned x1 = 0x0;
...
// '0' is useless, '2' is not needed. Trailing space is useless
// sscanf(str,"x ",&x1);
sscanf(str,"%x", &x1); // Check return value - not shown
or saved directly
sscanf(str, "%hhx", &pt[i]);
What is other than 3 values per line?
Sample code to read a line of 3 hex values and detect lots of errors.
#define EXPECTED_LINE_SIZE (3*3 1 /* for the \0 */)
char line[EXPECTED_LINE_SIZE * 2];
if (fgets(line, sizeof line, plaintext) == NULL) {
return failure;
}
int n = 0;
unsigned val[3];
sscanf(line "%2x %2x %2x %n", &val[0], &val[1], &val[2], &n);
if (n == 0 || line[n] != '\0') {
return failure;
}
Check if fopen()
failed
FILE *fp=fopen("rgb_values.txt","r");
FILE *fo=fopen("out.txt","w");
if (fp == NULL || fo == NULL) {
TBD_Error_out(); // Add your code here
}
CodePudding user response:
If you just want to copy from one file to the other, open in binary mode, and in a loop fread
a buffer of say 4KiB, and then fwrite
it to the other file. Quick and easy:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *in = fopen(..., "rb");
FILE *out = fopen(..., "wb");
if (!in || !out)
return EXIT_FAILURE;
char buffer[4096];
size_t nread;
while ((nread = fread(buffer, 1, sizeof buffer, in)) > 0)
fwrite(buffer, 1, nread, out);
}
Otherwise if you need to read it as strings use fgets
and fputs
in a loop:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *in = fopen(..., "r");
FILE *out = fopen(..., "w");
if (!in || !out)
return EXIT_FAILURE;
char line[256];
while (fgets(line, sizeof line, in) != NULL)
fputs(line, out);
}
Or as I mentioned, fgets
to read a whole line, sscanf
to parse out all three values, pass back to the calling function (together with a file read success status), and fprintf
all three values at once:
#include <stdio.h>
#include <stdlib.h>
int read_values(FILE *in, int *values)
{
char line[256];
if (fgets(line, sizeof line, in) == NULL)
return 0; // Failure to read
if (sscanf(line("%x %x %x", &values[0], &values[1], &values[2]) != 3)
return 0; // Failure to parse
return 1; // Success
}
void write_values(FILE *out, int *values)
{
fprintf(out, "x x x\n", values[0], values[1], values[2]);
}
int main(void)
{
FILE *in = fopen(..., "r");
FILE *out = fopen(..., "w");
if (!in || !out)
return EXIT_FAILURE;
int values[3];
while (read_values(in, values))
write_values(out, values);
}
Note that I don't close the files. If your program is doing more after this, then they should really be closed. If the program just exits, then the system will close them for us.