I am implementing the program to zip a file using a run-length encoding compression method. Are there any ways you can compare the characters in the file or compare two file pointers to do that?
I opened the file(zipfilename) that I want to zip and set the file pointer named ftozip to it. Then, I tried to count the number of each character using this file pointer as shown in the code below (if condition).
FILE *ftozip;
ftozip = fopen(argv[1],"r");//open the file that we are zipping
if (ftozip == NULL) {//if there is an error opening
perror("File cannot be opened ");
}
char zipfilename[30];
strcat(zipfilename, argv[1]);
strcat(zipfilename,".zip");
FILE *zipfilep = fopen(zipfilename, "a"); //zipfile openned to write
int count = 1;
while(1){ //incrementing the characters and storing in the zip file
if(*ftozip == *(ftozip +1)) {
count++;
char countchar[] = (char)count+(*ftozip);
fputs(countchar, zipfilep);
ftozip++;
continue;
}
else {
count = 1;
countchar = (char)count + (*ftozip);
ftozip++;
if (feop(ftozip){
break;
}
continue;
}
}
That resulting in this error "invalid operands to binary == (have ‘FILE’ and ‘FILE’)".
Dereferencing a pointer of type FILE*, as you do with if(*ftozip == *(ftozip +1) ..., does not access the contents of the file.
To read or write from or to a file bytewise, use fread and fwrite instead.
Related
Today I decided to learn to code for the first time in my life. I decided to learn C. I have created a small program that checks a txt file for a specific value. If it finds that value then it will tell you that that specific value has been found.
What I would like to do is that I can put multiple files go through this program. I want this program to be able to scan all files in a folder for a specific string and display what files contain that string (basically a file index)
I just started today and I'm 15 years old so I don't know if my assumptions are correct on how this can be done and I'm sorry if it may sound stupid but I have been thinking of maybe creating a thread for every directory I put into this program and each thread individually runs that code on the single file and then it displays all the directories in which the string can be found.
I have been looking into threading but I don't quite understand it. Here's the working code for one file at a time. Does anyone know how to make this work as I want it?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
//searches for this string in a txt file
char searchforthis[200];
//file name to display at output
char ch, file_name[200];
FILE *fp;
//Asks for full directory of txt file (example: C:\users\...) and reads that file.
//fp is content of file
printf("Enter name of a file you wish to check:\n");
gets(file_name);
fp = fopen(file_name, "r"); // read mode
//If there's no data inside the file it displays following error message
if (fp == NULL)
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
//asks for string (what has to be searched)
printf("Enter what you want to search: \n");
scanf("%s", searchforthis);
char* p;
// Find first occurrence of searchforthis in fp
p = strstr(searchforthis, fp);
// Prints the result
if (p) {
printf("This Value was found in following file:\n%s", file_name);
} else
printf("This Value has not been found.\n");
fclose(fp);
return 0;
}
This line,
p = strstr(searchforthis, fp);
is wrong. strstr() is defined as, char *strstr(const char *haystack, const char *needle), no file pointers in it.
Forget about gets(), its prone to overflow, reference, Why is the gets function so dangerous that it should not be used?.
Your scanf("%s",...) is equally dangerous to using gets() as you don't limit the character to be read. Instead, you could re-format it as,
scanf("%199s", searchforthis); /* 199 characters + \0 to mark the end of the string */
Also check the return value of scanf() , in case an input error occurs, final code should look like this,
if (scanf("%199s", searchforthis) != 1)
{
exit(EXIT_FAILURE);
}
It is even better, if you use fgets() for this, though keep in mind that fgets() will also save the newline character in the buffer, you are going to have to strip it manually.
To actually perform checks on the file, you have to read the file line by line, by using a function like, fgets() or fscanf(), or POSIX getline() and then use strstr() on each line to determine if you have a match or not, something like this should work,
char *p;
char buff[500];
int flag = 0, lines = 1;
while (fgets(buff, sizeof(buff), fp) != NULL)
{
size_t len = strlen(buff); /* get the length of the string */
if (len > 0 && buff[len - 1] == '\n') /* check if the last character is the newline character */
{
buff[len - 1] = '\0'; /* place \0 in the place of \n */
}
p = strstr(buff, searchforthis);
if (p != NULL)
{
/* match - set flag to 1 */
flag = 1;
break;
}
}
if (flag == 0)
{
printf("This Value has not been found.\n");
}
else
{
printf("This Value was found in following file:\n%s", file_name);
}
flag is used to determine whether or not searchforthis exists in the file.
Side note, if the line contains more than 499 characters, you will need a larger buffer, or a different function, consider getline() for that case, or even a custom one reading character by character.
If you want to do this for multiple files, you have to place the whole process in a loop. For example,
for (int i = 0; i < 5; i++) /* this will execute 5 times */
{
printf("Enter name of a file you wish to check:\n");
...
}
#include <stdio.h>
#include <stdlib.h>
int main(void) {
FILE *fp;
fp = fopen("clients.dat", "wb");
fclose(fp);
fp = fopen("clients.dat", "rb");
while (1) {
if (fp == EOF)
break;
else
printf("There is something inside a file");
}
fclose(fp);
return 0;
}
Here comes a question: what do empty binary files contain? should the pointer point to the EOF character? I mean: isn't it that the first and last thing in the file is EOF? OR how Can I check whether a file is empty or not?
An empty file contains nothing, it is empty. So it contains 0 bytes. EOF is not a character that is at the end of a file, it is an integer constant used as return value from some of the standard methods reading from a file to indicate end of file or some sort of error.
When you open a file you get a pointer to a FILE type back, this is what you can expect even from an empty file.
A file is not terminated the same way a string is, so there is no equivalent of a NULL character in a file, that determines when the file contents stops.
To determine whether a file you have opened and have a valid FILE pointer to is empty you can use fseek and ftell:
fseek(fp, 0, SEEK_END);
size = ftell(fp);
if (size == 0) {
// File is empty
}
Function fopen returns a pointer to a file handle of type FILE, not a pointer to any content of the file or a pointer to an EOF-character. The pointer is NULL if the file could not be opened at all, but does not indicate whether the file is empty or not.
To check if a file is empty you either (1) need to make an attempt to read bytes and handle the various results, or (2) to use fseek and ftell to move the read pointer to the end and ask then for the position.
(1)
fp=fopen("clients.dat","rb");
char buffer;
size_t bytesRead = fread(&buffer, 1, 1, fp); // try to read one byte
if(bytesRead == 1) {
printf("file contains at least one byte\n");
} else { // error handling
if (feof(fp))
printf("Attemt to read though end of file has been reached. File is empty.\n");
else if (ferror(fp)) {
perror("Error reading file.");
}
}
(2)
fp=fopen("clients.dat","rb");
fseek(fp, 0L, SEEK_END);
long size = ftell(fp);
if (size==0) {
// file is empty.
}
I'd prefer the second variant.
Here's another approach:
To check if the file is empty, you can simply read the file:
int c = fgetc(fp);
if (c == EOF)
{
// The file is empty or an error occured
if (feof(fp))
{
// Empty
}
else
{
// Error during file read
}
}
else
{
// non-empty file
}
Here comes a question what empty binary files contain ?
Empty files contain nothing, that is what makes them empty.
Regular files have a size which is not part of their data, but instead is normally a part of the directory entry or inode.
should the pointer point to the EOF character ?
No
First of all the pointer returned by fopen is NOT a pointer to the content of the file, but merely a pointer to a data structure describing the open file.
Secondly EOF is not an actual part of the file, but a special return value from the getc family of functions used to indicate that the end of file has been reached.
To test whether you are at the end of a file without reading from it you can use the feof function.
I need to read a text file with 7 lines into 7 different variables. The text file looks like this:
.2661
A.txt
B.txt
C.txt
1
2
0.5 0.6
These are the variables that I need to store each line into:
float value1; // line 1 from .txt file
char *AFileName; // line 2 from .txt file
char *BFileName; // line 3 from .txt file
char *CFileName; // line 4 from .txt file
int value2; // line 5 from .txt file
int lastLineLength; // line 6 from .txt file
double lastLine[lastLineLength]; // line 7 from .txt file - this can be different lengths
I have currently been doing this by just using the arguments when I call my program from the command line and the argv command.
First open the file using fopen with read access:
FILE *inputFile = fopen(filename, "r");
if(!inputFile) {
// Error opening file, handle it appropriately.
}
Then read the data from the file using fscanf. The first parameter is the FILE * we created above. The second parameter is a format string that specifies what fscanf should expect while reading the file. The remaining parameters are pointers to variables that will hold the data read from the file.
int variablesFound;
variablesFound = fscanf(inputFile, "%f\n%s\n%s\n%s\n%d\n%d\n", &value1, AFileName, BFileName, CFileName, &value2, &lastLineLength);
if(variablesFound < 6) {
// There was an error matching the file contents with the expected pattern, handle appropriately.
}
double lastLine[lastLineLength];
// Iterate over the last line.
int lastLineIndex;
for(lastLineIndex = 0; lastLineIndex < lastLineLength; lastLineIndex++) {
fscanf(inputFile, "%lf", &lastLine[lastLineIndex]);
fscanf(inputFile, " "); // Eat the space after the double.
}
Edit
After comments I realized it might be worth noting that you have to allocate memory to your variables as the real first step. The primitives (those with an & below) can be declared as normal. For the string (char array), you'll want to do one of the following:
char *aFileName = calloc(MAX_FILENAME_SIZE + 1, sizeof(char));
or
char aFileName[MAX_FILENAME_SIZE + 1];
Depending on what your purpose with aFileName would be determines which method would be appropriate. However, assuming this code appears in the main or doesn't need to exist beyond the scope of the function, the latter would be better as it doesn't require free()ing the variable after you're done with it.
It also may be worth while singling out the code that deals with reading input if your requirements change often.
You can read from the file as follows:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 80;
fp = fopen("<path to your file>", "r");
if (fp == NULL)
exit(-1);
while (getline(&line, &len, fp) != -1)
printf("%s", line);
fclose(fp);
return 0;
}
getline reads character strings from the file, so you'd have to parse the lines as needed (atoi, atof).
I created a function that is successfully reading the binary file but it is not printing as I wanted.
The function:
void print_register() {
FILE *fp;
fp = fopen("data.bin", "rb");
if (fp == NULL) {
error_message("Fail to open data.bin for reading");
exit(0);
}
reg buffer;
while (EOF != feof(fp)) {
fread(&buffer, sizeof(reg), 1, fp);
printf("%s %d %d\n", buffer.name, buffer.age, buffer.id);
}
fclose(fp);
}
Note: reg is a typedef for a struct:
typedef struct registers reg;
struct registers {
char name[30];
int age;
int id;
char end;
};
Function for writing the file:
void register_new() {
system("clear");
reg buffer;
FILE *fp;
fp = fopen("data.bin", "ab");
if (fp == NULL) {
error_message("Error opening file data.bin");
exit(0);
}
write_register(buffer);
fwrite(&buffer, sizeof(reg), 1, fp);
fclose(fp);
}
Posting a printscreen of what was print to be more helpful:
As you can see on image, after the "p" (command for printing) is where should be the name, age and id of the struct.
In register_new(), you have to send the address of buffer in order for write_register() to work properly (right now you're giving it a copy of buffer).
Replace:
write_register(buffer);
with:
write_register(&buffer);
Then correct write_register to take and work with an address instead of a structure.
This might help you understand what's going on: http://fresh2refresh.com/c-programming/c-passing-struct-to-function
Your reading loop is incorrect. Don't use feof(), it can only tell is you have reached the end of file after a read attempt failed and it might not return EOF anyway, it is only specified as returning 0 or non 0. Use this instead:
while (fread(&buffer, sizeof(reg), 1, fp) == 1) {
printf("%s %d %d\n", buffer.name, buffer.age, buffer.id);
}
fread returns the number of items successfully read. Here you request to read 1 item of size sizeof(reg), if the item was read successfully, fread will return 1, otherwise it will return 0 (in case of a read error or end of file reached).
Your screenshot shows a syntax error, which you seem to have fixed now. Remove that, it is not helping.
In your function register_new, you are writing an uninitialized structure reg to the file, no wonder it does not contain anything useful when you read it back from the file. And for what it is worth, opening this file in binary mode is the correct thing to do since it contains binary data, namely the int members of the structure.
The reg passed to fwrite is indeed uninitialized. write_register gets a copy of this uninitialized structure by value, and probably modifies this copy, but this does not affect the local structure in register_new. You should modify write_register() to take a pointer to the structure. Unlike C++, there is no passing by reference in C.
I have written the following code (as part of ab assignment for university) in an attempt to save 1 int to a text file (using fprintf) and an array of structs of type Flight to a .bin file. It seems that both are remaining empty. I am calling the read at the beginning of the program and the write on exit or when the Save option is selected. The read must first take the int value as it is a crucial counter for how many elements there are (and therefore how many need to be read / written).
I have looked at other answers, and even based some of my code off them however after reading and re-reading I still could not find a solution hence i posted a new question.
This is the Write Section, The files are flyC.txt (to store the counter) and fly.bin to store the array of structs.
void writeFlight (){
FILE * cpt;
if ((cpt = fopen("flyC.txt", "wb")) == NULL)
{
printf("ERROR: Flight Count File Could Not Be Opened / Written To \n");
}
fprintf(cpt, "%d", curFly);
FILE * fpt;
if ((fpt = fopen("fly.bin", "wb")) == NULL)
{
printf("ERROR: Flight File Could Not Be Opened / Written To \n");
}
fwrite(flyList, curFly * sizeof(struct Flight), 1, fpt);
fclose(fpt);
}
And here is the Read section.
void readFlight(){
//First Read file with variable curFly
FILE * cpt;
if((cpt = fopen("flyC.txt", "rb")) == NULL)
{
printf("ERROR: Flight Count File Could Not Be Opened\n");
}
if (1 != fscanf(cpt, "%d", &curFly)){
printf("ERROR: Flight Count File Could Not Be Read\n");
}
fclose(cpt);
FILE * fpt;
if((fpt = fopen("fly.bin", "rb")) == NULL)
{
printf("ERROR: Flight File Could Not Be Opened / Read\n");
}
fread(flyList, sizeof(struct Flight) * curFly, 1, fpt);
fclose(fpt);
}
Any help is appreciated!
I see several issues here.
I understood that flyC.txt was supposed to contain plain ASCII text rather than binary data. If this is the case, than you shouldn't use "wb" when opening cpt for writing, but just "w"
While writing cpt you don't close it. The OS might close it for you at the end of the program, but you shouldn't rely on this.
While reading the file back, you've done a similar mistake for flyC.txt; you shouldn't open it using “rb" but just "r" if it contained binary data.
You forgot fclose(cpt); in the write function.
Also you should return on error and not call fread/fwrite.