Read file block by block in C - c

I want to copy the contents of file1 to file2 exactly as they are (keeping spaces and newlines). I specifically want to copy these contents one small block of chars at a time(this is a small segment of a larger project so bear with me).
I have attempted the following:
#include <stdio.h>
#include <stdlib.h>
#define MAX 5
int main(int argc, char *argv[]) {
FILE *fin, *fout;
char buffer[MAX];
int length;
char c;
if((fin=fopen(argv[1], "r")) == NULL){
perror("fopen");
exit(EXIT_FAILURE);
}
if((fout=fopen(argv[2], "w")) == NULL){
perror("fopen");
exit(EXIT_FAILURE);
}
while(1){
length = 0;
while((c = fgetc(fin)) != EOF && length < MAX){
buffer[length++] = (char) c;
}
if(length == 0){
break;
}
fprintf(fout, "%s", buffer);
}
fclose(fout);
fclose(fin);
}
However, this causes incorrect output to my file2. Any input would be appreciated.

Your buffer is not zero-terminated. Use fwrite instead of fprintf:
fwrite(buffer, 1, length, fout);
And you should check the error too. So compare return code of fwrite to length and if it differs, either retry the write of remaining bytes (if positive) or print appropriate error message via perror("fwrite") (if return code is negative).
Additionally you may consider opening the files in binary mode which would cause difference on windows, i.e. pass "rb" and "wb" to fopen.
Last but not least, instead of looping and getting one character at a time, consider using fread instead:
length = fread(buffer, 1, MAX, fin);

Here is a simple example.(with no error checking)
You should use fwrite() since the string you would write to file is not a "null-terminated". And also note that "b" mode is specified with fopen(), which means you want to open the file as a binary file.
#include <stdio.h>
#include <stdlib.h>
#define MAX 5
#define FILE_BLOCK_SIZE 50
int _tmain(int argc, _TCHAR* argv[])
{
FILE *fin, *fout;
unsigned char *BufContent = NULL;
BufContent = (unsigned char*) malloc(FILE_BLOCK_SIZE);
size_t BufContentSz;
if((fin=fopen("E:\\aa.txt", "rb")) == NULL){
perror("fopen");
exit(EXIT_FAILURE);
}
if((fout=fopen("E:\\bb.txt", "wb")) == NULL){
perror("fopen");
exit(EXIT_FAILURE);
}
while ((BufContentSz = fread(BufContent, sizeof(unsigned char), FILE_BLOCK_SIZE, fin)) > 0)
{
fwrite(BufContent, sizeof(unsigned char), BufContentSz, fout);
}
fclose(fout);
fclose(fin);
delete BufContent;
return 0;
}

First off, change char buffer[MAX]; to int buffer[MAX];, and char c; to int c;, for a char can be either signed char or unsigned char, depending on your implementation. In the later case, c = EOF will give c a large positive number(It's unsigned ,anyway), so the loop will never end. A int will be large enough to hold all characters and EOF though.
Then, change your
fprintf(fout, "%s", buffer);
to
fwrite(buffer, 1, length, four);
This is because fprintf(fout, "%s", buffer); call for a C-style string, with ends with a '\0', but your buffer isn't zero-terminated. As a result, the program will keep copying the stuff in the stack, until a '\0' is met, leaving lots of garbage in file2.

Related

Copy Function in C not creating matching Checksums

I written a simple copy program that copies a file and generates an MD5, It runs and generates the MD5 correctly.
However when verifying the file generated by the copy function it does not match the source MD5. I can't see any reason for this in my code, can anyone help?
#include <stdio.h>
#include <openssl/md5.h>
#include <assert.h>
#define BUFFER_SIZE 512
int secure_copy(char *filepath, char *destpath);
int main(int argc, char * argv[]) {
secure_copy(argv[1], argv[2]);
return 0;
}
int secure_copy(char *filepath, char *destpath) {
FILE *src, *dest;
src = fopen(filepath, "r");
assert(src != NULL);
dest = fopen(destpath, "w");
assert(dest != 0);
MD5_CTX c;
char buf[BUFFER_SIZE];
ssize_t bytes, out_writer;
unsigned char out[MD5_DIGEST_LENGTH];
MD5_Init(&c);
while((bytes = fread(buf, 1, BUFFER_SIZE, src)) != 0) {
MD5_Update(&c, buf, bytes);
out_writer = fwrite(buf, 1, BUFFER_SIZE, dest);
assert(out_writer != 0);
}
MD5_Final(out, &c);
printf("MD5: ");
for (int i=0; i < MD5_DIGEST_LENGTH; i++)
{
printf("%02x", out[i]);
}
printf("\n");
fclose(src);
fclose(dest);
return 0;
}
Output
$ ./md5speed doc.txt /home/doc.txt
MD5: 4c55e4b9185eece3cc000c4023f8f6fe
when verifying the copied file with md5sum I get a completely different hash.
md5sum doc.txt
29cb4da30c3e28fdb81463b5f0a76894 doc.txt
Though the file still opens and content is uncorrupted.
regarding:
while((bytes = fread(buf, 1, BUFFER_SIZE, src)) != 0)
and
out_writer = fwrite(buf, 1, BUFFER_SIZE, dest);
on the last read, the amount read can be less than BUFFER_SIZE so should always use bytes variable for the number of bytes to write.
Also, certain errors can occur when calling fread() and/or fwrite() Such errors are indicated by negative values (and/or values less than the 3rd parameter to those functions) in the returned variables (bytes, outwriter). The code, to be robust, must be checking those values and handling any errors that occur, including EOF
As stated in comments, changing the fwrite function to use bytes as opposed to BUFFER_SIZE combined with changing file operations mode "rb" and "wb" to binary.

fread is not reading other file formats

I am fairly new to C still, but the program below compiles just fine, (using gcc) and it even works when using text files, but I when I use other file formats, i.e. png, I get nothing. The console spits out ?PNG and nothing else. I don't want the image to print as an image, obviously the program does nothing like that, but I would like the data from the png file to be printed. Why is the program not fread-ing properly? Is is because fread refuses any file other than text?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
FILE *fp;
int main() {
char buffer[1000];
fp=fopen("FILE IN QUESTION HERE", "rb");
if(fp==NULL) {
perror("An error occured while opening the file...");
exit(1);
}
fread(buffer, 1000, 1, fp);
printf("%s\n", buffer);
fclose(fp);
return 0;
}
%s in printf() is for printing null-terminated string, not binary data and PNG header contains a signature to prevent the data from being transfered as text by mistake.
(Actually there are no 0x00 in the PNG signature and printf() stopped at the 0x00 contained in the size of IHDR chunk)
Use fwrite() to output binary data, or print the bytes one-by-one via putchar().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
FILE* fp; /* avoid using gloval variables unless it is necessary */
char buffer[1000] = {0}; /* initialize to avoid undefined behavior */
fp=fopen("FILE IN QUESTION HERE", "rb");
if(fp==NULL) {
perror("An error occured while opening the file...");
exit(1);
}
fread(buffer, 1000, 1, fp);
fwrite(buffer, 1000, 1, stdout); /* use fwrite instead of printf */
fclose(fp);
return 0;
}
fread is not reading other file formats
Code does not check the result of fread(). That is the way to determine if fread() is working.
char buffer[1000];
// fread(buffer, 1000, 1, fp);
size_t sz = fread(buffer, 1000, 1, fp);
if (sz == 0) puts("Did not read an entire block");
fread() returns the number of blocks read. With OP's case, code is attempting to read one 1000 byte block. Recommend reading 1000 blocks, each of 1 char rather than 1 block of a 1000 char. Further, avoid magic numbers.
for (;;) {
size_t sz = fread(buffer, sizeof buffer[0], sizeof buffer, fp);
if (sz == 0) break;
// Somehow print the buffer.
print_it(buffer, sz);
}
OP call to printf() expects a pointer to a string. A C string is an array of characters up to and including the terminating null character. buffer may/may not contain a null character and useful data after a null character.
// Does not work for OP
// printf("%s\n", buffer);
The data of a .png file is mostly binary and will have little textual meaning. A sample print function of mixed binary data and text follows. Most output will appears meaningless until one learns the .png file format. Untested code.
int print_it(const unsigned char *x, size_t sz) {
char buf[5];
unsigned column = 0;
while (sz > 0) {
sz--;
if (isgraph(*x) && *x != `(`) {
sprintf(buf, "%c", *x);
} else {
sprintf(buf, "(%02X)", *x);
}
column += strlen(buf);
if (column > 80) {
column = 0;
fputc('\n', stdout);
}
fputs(buf, stdout);
}
if (column > 0) fputc('\n', stdout);
}

How can i select the last line of a text file using C

I am trying to find out a way to select the last line of a text file using C (not c++ or c#, just C) and I am having a difficult time finding a way to do this, if anyone could assist me with this problem I would be very grateful, thanks! (btw for a good example of what i am trying to do, this would be similar what to tail -n 1 would be doing in bash)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(int argc, char *argv[])
{
FILE *fd; // File pointer
char filename[] = "./Makefile"; // file to read
char buff[1024];
if ((fd = fopen(filename, "r")) != NULL) // open file
{
fseek(fd, 0, SEEK_SET); // make sure start from 0
while(!feof(fd))
{
memset(buff, 0x00, 1024); // clean buffer
fscanf(fd, "%[^\n]\n", buff); // read file *prefer using fscanf
}
printf("Last Line :: %s\n", buff);
}
}
I'm using Linux.
CMIIW
No direct way, but my preferred method is:
Go to the end of the file
Read last X bytes
If they contain '\n' - you got your line - read from that offset to the end of the file
Read X bytes before them
back to 3 until match found
If reached the beginning of the file - the whole file is the last line
E.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef max
#define max(a, b) ((a)>(b))? (a) : (b)
#endif
long GetFileSize(FILE *fp){
long fsize = 0;
fseek(fp,0,SEEK_END);
fsize = ftell(fp);
fseek(fp,0,SEEK_SET);//reset stream position!!
return fsize;
}
char *lastline(char *filepath){
FILE *fp;
char buff[4096+1];
int size,i;
long fsize;
if(NULL==(fp=fopen(filepath, "r"))){
perror("file cannot open at lastline");
return NULL;
}
fsize= -1L*GetFileSize(fp);
if(size=fseek(fp, max(fsize, -4096L), SEEK_END)){
perror("cannot seek");
exit(1);
}
size=fread(buff, sizeof(char), 4096, fp);
fclose(fp);
buff[size] = '\0';
i=size-1;
if(buff[i]=='\n'){
buff[i] = '\0';
}
while(i >=0 && buff[i] != '\n')
--i;
++i;
return strdup(&buff[i]);
}
int main(void){
char *last;
last = lastline("data.txt");
printf("\"%s\"\n", last);
free(last);
return 0;
}
If you are using *nix operating system, you can use the command 'last'. See 'last' man page for details.
If you want integrate the functionality inside another program, you can use 'system' call to execute 'last' and get it's result.
A simple and inefficient way to do it is to read each line into a buffer.
When the last read gives you EOF, you have the last line in the buffer.
Binyamin Sharet's suggestion is more efficient, but just a bit harder to implement.

Encrypting a simple array

For a lab I've got to do, I need to create a program that will take a simple string from a text file and encrypt it using a key - a number between 0 and 255. It will read the file into an array and encrypt (or decrypt) this array into another array by XOR-ing each byte with the key. In the end, it writes the modified array into a second file.
I've mostly got it - what I have below compiles just fine. It doesn't, however, copy anything to the second file. Help!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CRYPT(a, b) (a ^ b)
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
int a[100], b, key;
int i = 0;
// opens file containing string to be encrypted
if((fp1 = fopen(argv[2], "rb")) == NULL)
{
printf("Error - could not open file or file does not exist\n");
return;
}
// opens file encrypted string will be saved to
fp2 = fopen(argv[3], "wb");
// converts string to integer
key = atoi(argv[1]);
while(fread(a, sizeof(a), 100, fp1))
{
while (i != '\0');
{
b = CRYPT(a[i], key);
fwrite(&b, sizeof(a), 1, fp2);
i++;
}
}
return 0;
}
I think the problem lies here -
while (i != '\0');
You are initializing i to 0 and in the while loop you are checking whether or not i is equal to NULL. The integer value of NULL, or \0 is 0. As a result, the expression is false and your loop is never being executed.
Also remove the extra semi-colon at the end of this while loop.
From the reference -
size_t fread ( void * ptr, size_t size, size_t count, FILE * stream );
Reads an array of count elements, each one with a size of size bytes, from the stream and stores them in the block of memory specified by ptr.
The postion indicator of the stream is advanced by the total amount of bytes read.
The total amount of bytes read if successful is (size * count).
So you also need to change your fread function to this -
fread(a, sizeof(int), 100, fp1)
Similarly, you also need to change your fwrite -
fwrite(&b, sizeof(int), 1, fp2);
The edited code should look something like this -
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define CRYPT(a, b) (a ^ b)
int main(int argc, char *argv[])
{
FILE *fp1, *fp2;
int a[100], b, key;
int i = 0;
int data_read = 0;
// opens file containing string to be encrypted
if((fp1 = fopen(argv[2], "rb")) == NULL)
{
printf("Error - could not open file or file does not exist\n");
return;
}
// opens file encrypted string will be saved to
fp2 = fopen(argv[3], "wb");
// converts string to integer
key = atoi(argv[1]);
while( (data_read = fread(a, sizeof(int), 100, fp1)) > 0 )
{
while(i < data_read)
{
b = CRYPT(a[i], key);
fwrite(&b, sizeof(int), 1, fp2);
i++;
}
i=0;
}
return 0;
}
There are a few serious flaws with your code.
First and foremost there is a buffer overflow on integer array a (i.e. more than 100 integers can be read into a).
As Thomas McCarthy commented the semi-colon at the end of your while-loop creates an empty statement - remove it.
Also, you are writing sizeof(a) or 100 integers into fp2 for every character.

How to print content of file after seek

I am trying to print the remaining contents of a file after I do a fseek. Right now I am getting nothing returned. What's wrong with my code?
#include <stdio.h>
int main(int argc, char *argv[]){
FILE *fr;
if (fr = fopen (argv[1], "r")){
fseek(fr, 100, SEEK_CUR);
char c[1];
while (fread(c, 1, sizeof(c),fr) > 0)
printf("%s", c);
fclose(fr);
}
else{
perror("File does not exist");
}
}
As the other answers pointed out, you are passing printf a string which may not be NULL terminated. You are also not verifying the file being read is greater than 100 bytes. One last point, in fread() you swapped the size_t size and size_t niters parameters.
Here is an modified version of your program which fixes the above issues (and cleans up the spacing a bit):
#include <stdio.h>
#include <sys/stat.h>
int main(int argc, char *argv[])
{
FILE *fr;
char c[1];
struct stat sb;
// obtains information about the file
if (stat(argv[1], &sb) == -1)
{
perror("stat()");
return(1);
};
// verifies the file is over 100 bytes in size
if (sb.st_size < 101)
{
fprintf(stderr, "%s: file is less than 100 bytes\n", argv[1]);
return(1);
};
// opens the file, or prints the error and exists
if (!(fr = fopen (argv[1], "r")))
{
perror("fopen():");
return(1);
};
fseek(fr, 100, SEEK_CUR);
while (fread(c, sizeof(c), 1, fr) > 0)
printf("%c", c[0]);
fclose(fr);
return(0);
}
You could also improve the efficiency of reading the file by changing char c[1]; to something line char c[1024]; and updating the while statement to:
while (fread(c, sizeof(char), 1023, fr) > 0)
{
c[1023] = '\0';
printf("%s", c);
};
you cannot print with %s as your string needs to be null terminated and you only have one character.
use:
printf("%c",*c);
not all characters are printable, check an ascii table to see which are printable and which not. eg printing a 0 will not print anything on screen, AFAIK
You are reading a byte but trying to print it with %s, which expects a null-terminated string. Changing that to %c (and of course c to *c so that it agrees with the format string!) should fix things.
Especially if the value of the bytes you read is equal to 0, printf with the %s specifier will output absolutely nothing (as it thinks you are repeatedly asking it to print the empty string).
printf("%s",blabla) should print a null-terminated string. for print one char use printf("%c",c[0]).

Resources