variadic function- reading files (c) - c

i need to create a variadic function (stdarg library) which will loop through all the files i pass it and will count words similiar to a word i pass as a parameter,
#include <stdio.h>
#include <stdarg.h>
void countWords(char* name, FILE* file, ...){
va_list params;
FILE* currentFile;
FILE* f;
int words = 0;
va_start(params, file);
currentFile = file;
while (currentFile != NULL)
{
f = fopen(currentFile, "r+"); //which file should i open every time? this doesnt compile
// comparing words in each file code
currentFile = va_arg(params, FILE*);
}
va_end(params);
}
i cant read the file (no metter what i try it doesnt compile),
and how can i loop through each file comparing my word? i would really appreciate guidance
thank you!

If you are passing the file name or more precisely the path to the file, then this
FILE *currentFile;
currentFile = va_arg(params, FILE *);
should be
char *currentFile;
currentFile = va_arg(params, char *);
If you pass FILE pointers, you should not open them because if the rest of the program is correct then they shall be already opened inside the funcion, otherwise it doesn't make any sense to pass FILE *'s.
So the function should probably be
#include <stdio.h>
#include <stdarg.h>
void countWords(char *word, char *filename, ...)
{
va_list params;
FILE *file;
int words;
words = 0;
va_start(params, file);
while (filename != NULL)
{
file = fopen(filename, "r+");
// comparing words in each file code
filename = va_arg(params, char *);
}
va_end(params);
}
You would call it like this
countWords("example", "/path/to/file/1", ..., "/path/to/file/n", NULL);
and you should be careful with string literals probably use the const qualifier in this situation, because even if the parameters are not string literals it wouldn't make sense to modify them inside countWords() so to prevent accidentally modifying them const could help, although you can always modify them anyway. Even if modifying a string literal invokes undefined behavior you cannot completely forbid your program from doing so.

i need to create a variadic function
Unless this is a homework assignment about variadic functions, you don't need a variadic function at all. Variadic functions are not type safe and type safety is a desirable property.
Others have already pointed this out: You need a function that takes a filename and counts the occurrences of a word in a single file
size_t occurrences(const char *word, const char *filename) { ... }
You can then easily loop over an array of files, e.g.:
size_t count = 0;
for (int i = 1, i < argc; i++) {
size += occurrence("pink", argv[i]);
}
If you like, you can wrap this in a separate function which takes an array of file names with a file count. This is easily done, because whether you take your files from a command line or from somewhere else, you already have them stored in some kind of array.
Now consider the variadic variant, which makes sense only if you know the files you want to process at compile time, e.g.:
size_t n = var_occurrences("banana",
"alpha.txt", "beta.txt", "gamma.txt", NULL);
The same can be achieved with the non-variadic approach:
const char *w = "banana";
size_t n = occurrences(w, "alpha.txt")
+ occurrences(w, "beta.txt")
+ occurrences(w, "gamma.txt");
This is a bit more typing, but everything else is much more straightforward. If you must use variadic functions, go ahead and look at iharob's answer. But variadic functions are not a good solution to your task.

Related

How to overwrite a preallocated char array in C

I initialize a char array as a global variable in my C program with a default file location/name as
char file[] = "/home/jack/files/data.txt";
Later in the program, if a condition is satisfied I read a file containing a new file name
int read_new_file(char *fname)
{
FILE *inp;
char buffer[255];
char oldFile[127], newFile[127];
inp = fopen(fname, "r");
while ( fgets(buffer, 255, inp) != NULL )
{
sscanf(buffer, "%s %s",oldFile,newFile);
file = newFile; // <---- Is this wrong/unsafe?
}
return 0;
}
I should note that it is assumed file fname only contains one line with 2 strings. This was just to show the general framework of the code. My question, as highlighted in the code, is it wrong or unsafe to simply try and reassign the char file variable with the new string?
The size of the newFile string will likely differ from it's original default size. The default has to be predefined, since the condition may not require a new file to be read. If the assignment is wrong or unsafe, what is a better approach?
You need to declare file so it's large enough to hold newFile.
char file[127] = "/home/jack/files/data.txt";
Then when you want to update it, you use:
strcpy(file, newFile);
file = newFile; // <---- Is this wrong/unsafe?
It is wrong since file is an array. The compiler won't let you do that.
Whether it is safe or not is not relevant since you can't do it.
Use strcpy instead.
strcpy(file, newFile);

Reading in char from file into struct

For my assignment, I have to read in a text file with a varying amount of lines. They follow the following format:
AACTGGTGCAGATACTGTTGA
3
AACTGGTGCAGATACTGCAGA
CAGTTTAGAG
CATCATCATCATCATCATCAT
The first line is the original line I will testing the following ones against, with the second line giving the number of remaining lines.
I'm having trouble trying to save these to a struct, and can't even get the first line to save. I tried using the void function with an array and it seems to work, but can't seem to transfer it over to structs.
Here's my code so far:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LENGTH 25
struct dna {
char code[LENGTH];
};
int main(){
char filename[] = "input1.txt";
FILE *input = fopen("input1.txt","r");
char firstDna[LENGTH]="";
struct dna first;
struct dna first.code[]= "";
makeArray(input,first);
// printf("%s",filename);
system("pause");
return 0;
}
void makeArray(FILE *input,struct dna first){
int i=-1;
//nested for loops to initialze array
//from file
while(i != '\n'){
fscanf(input,"%c",first[i].code);
printf("%c", first[i].code);
i++;
}//closing file
fclose(input);
}
Since this is for a class assignment, I want to preface this by saying that a good way to tackle these types of assignments is to break it up into tasks, then implement them one by one and finally connect them. In this case the tasks might be something like:
parse the first line into a (struct containing a) char array.
parse the number into an int variable
parse each remaining line in the file like you did with the first line
test the first line against the other lines in the file (except the number)
You also mentioned in a comment that the struct is for extra credit. For that reason, I'd recommend implementing it using just a char array first, then refactoring it into a struct once you have the basic version working. That way you have something to fall back on just in case. This way of developing might seem unnecessary at this point, but for larger more complicated projects it becomes a lot more important, so it's a really good habit to get into as early as possible.
Now, let's look at the code. I'm not going to give you the program here, but I'm going to identify the issues I see in it.
Let's start with the main method:
char filename[] = "input1.txt";
FILE *input = fopen("input1.txt","r");
This opens the file you're reading from. You're opening it correctly, but the first line is in this case unnecessary, since you never actually use the filename variable anywhere.
You also correctly close the file at the end of the makeArray function with the line:
fclose(input);
Which works. It would, however, probably be better style if you put this in the main method after calling the makeArray function. It's always a good idea to open and close files in the same function if possible, since this means you will always know you didn't forget to close the file without having to look through your entire program. Again, not really an issue in a small project, but a good habit to get into. Another solution would be to put the fopen and fclose functions in the makeArray function, so main doesn't have to know about them, then just send the char array containing the filepath to makeArray instead of the FILE*.
The next issue I see is with how you are passing the parameters to the makeArray function. To start off, instead of having a separate function, try putting everything in the main method. Using functions is good practice, but do this just to get something working.
Once that's done, something you need to be aware of is that if you're passing or returning arrays or pointers to/from functions, you will need to look up the malloc and free functions, which you may not have covered yet. This can be one of the more complex parts of C, so you might want to save this for last.
Some other things. I won't go into detail about these but try to get the concepts and not just copy paste:
struct dna first.code[]= ""; should probably be first.code[0] = \0;. \0 is used in C to terminate strings, so this will make the string empty.
Passing %c to fscanf reads a single character (you can also use fgetc for this). In this case, it will probably be easier using %s, which will return a word as a string.
Assuming you do use %s, which you probably should, you will need to call it twice before the loop - once to get the first DNA sequence and another time to get the number of other DNA sequences (the number of iterations).
Each iteration of the loop will then test the original DNA sequence against the next DNA sequence in the file.
I hope that helps!
sample to fix
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LENGTH 25
struct dna {
char code[LENGTH];
};
struct dna *makeArray(FILE *input, int *n);//n : output, number of elements
int main(void){
char filename[] = "input1.txt";
FILE *input = fopen(filename,"r");
struct dna first = { "" };
fscanf(input, "%24s", first.code);//read first line
printf("1st : %s\n", first.code);
int i, size;
struct dna *data = makeArray(input, &size);//this does close file
for(i = 0; i < size; ++i){
printf("%3d : %s\n", i+1, data[i].code);
}
free(data);//release data
system("pause");
return 0;
}
struct dna *makeArray(FILE *input, int *n){//n : output, number of elements
int i;
fscanf(input, "%d", n);//read "number of remaining lines"
struct dna *arr = calloc(*n, sizeof(struct dna));//like as struct dna arr[n] = {{0}};
for(i = 0; i < *n; ++i){
fscanf(input, "%24s", arr[i].code);
}
fclose(input);
return arr;
}
a simple fix might be :
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define LENGTH 25
struct dna {
char code[LENGTH];
};
void makeArray(FILE *input,struct dna *first){
int i=0;
fscanf(input,"%c",&first->code[i]);
printf("%c",first->code[i]);
while(first->code[i] != '\n' && i < LENGTH){
i++;
fscanf(input,"%c",&first->code[i]);
printf("%c",first->code[i]);
}
}
int main() {
struct dna first;
char filename[] = "input1.txt";
FILE *input = fopen(filename,"r");
makeArray(input,&first);
fclose(input);
printf("%s",first.code);
return 0;
}
PS: i tried to not change your original code
in order to change the code[Length] in the makeArray function you will have to pass it's adresse this is why i call mkaeArray function this way : makeArray(input,&first);.

How read lines of file and get them to array in C?

I would like to create a function to read file line by line. One every line is one name.
int readConfig(char ** path, FILES ** files )
{
FILE* f;
f = fopen("file", "r");
int ch;
while ((ch=fgetc(f)) != EOF )
{
}
return 0;
}
How to use the fgetc function to parse the file? And how to get the results to the files[count].name?
Right off the bat:
char configFile [11] = "kernels.cfg";
[11] is too small. Try:
char configFile [12] = "kernels.cfg";
or
char configFile [] = "kernels.cfg"; /* let the compiler do the counting */
Also char is too small for ch -- use:
int ch;
You may also find fgets() -- which reads a whole line at at time -- simpler to use than fgetc().
You are getting SIGSEGV because of modifying string literal and that causes an undefined behavior (e.g. your SIGSEGV). I am not sure what should be stored in filename and name variables. If by line:
strcpy(files[count].filename,'.bin');
you've meant to add a '.bin' to filename variable, then this approach is wrong. You should use strcat. strcpy would write to filename from beginning of this variable, so some chars previously saved there would be overwritten. strcpy also adds a null termination char, so if you wanted to print it out, printf would stop on that \0 char and won't go further. However, the real problem is that you should allocate with malloc some space for your variables in struct. Then you will be able to modify them.
Consider simple example:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct file {
char* name;
char* filename;
};
int main(void)
{
struct file x;
x.name = malloc(30);
x.filename = malloc(40);
strncpy(x.name, "copied_string", 13);
printf("%s\n", x.name);
strcat(x.name, "_suffix");
printf("%s\n", x.name);
strcpy(x.name, "erased");
printf("%s\n", x.name);
free(x.name);
free(x.filename);
return 0;
}
output:
copied_string
copied_string_suffix
erased
This should make it a little more clear what's the origin of your crash. You should also consider using fgets or getline. Remember to free what you've malloc'd.
EDIT:
Calling readConfig(&path, &files); results in passing to readConfig a pointer of type FILES (*)[256]. Consider changing FILES files[256]; to
FILES* files = malloc(sizeof(FILES)*256);
and later call your function like readConfig(&path, files);
Then you would pass to readConfig function a compatible type of files.

How to make backslash character not to escape

I don't know the title correctly addresses my problem or not. So, I will just go with it.
Here is the problem, I have to input a char array of a file path (in Windows) containing lots of backslashes in it, eg. "C:\myfile.txt" and return an unsigned char array of C-style file paths, eg. "C:\myfile.txt".
I tried to write a function.
unsigned char* parse_file_path(char *path);
{
unsigned char p[60];
int i,j;
int len = strlen(path);
for(i=0,j=0; i<len; i++, j++)
{
char ch = path[i];
if(ch==27)
{
p[j++]='\\';
p[j]='\\';
}
else
p[j] = path[i];
}
p[j]='\0';
return p;
}
The weird thing (for me) I am encountering is, here path contains only one backslash '\'. In order to get one backslash, I have to put '\' in path. This is not possible, cause path cannot contain '\'. When I call it like this parse_file_path("t\es\t \it), it returns
t←s it. But parse_file_path("t\\es\\t \\it") returns t\es\t \it.
How can I accomplish my task? Thanks in advance.
If I can just mention another problem with your code.
You are returning a local variable (your unsigned char p). This is undefined behavior. Consider declaring a char* p that you assign memory to dynamically using malloc and then returning p as you do. E.g. something like:
char* p = malloc(60);
A common practice is to use sizeof when allocating memory with malloc but here I've passed 60 directly as the C standard guarantees that a char will be 1 byte on all platforms.
But you have to free the memory assigned with malloc.
Or alternatively, you can change the function to take a buffer as an input argument that it then writes to. That way you can pass a normal array where you would call this function.
Regarding your slashes issue, here:
p[j++]='\\';
p[j]='\\';
Position j in p will be changed to \\, then j will be incremented and at the very next line you do the same for the succeeding char position. Are you sure you want the two assignments?
By the way if you are inputting the path from the command line, the escaping will be taken care of for you. E.g. consider the following code:
#include <stdio.h>
#include <string.h> /* for strlen */
#include <stdlib.h> /* for exit */
int main()
{
char path[60];
fgets(path, 60, stdin); /* get a maximum of 60 characters from the standard input and store them in path */
path[strlen(path) - 1] = '\0'; /* replace newline character with null terminator */
FILE* handle = fopen(path, "r");
if (!handle)
{
printf("There was a problem opening the file\n");
exit(1); /* file doesn't exist, let's quite with a status code of 1 */
}
printf("Should be good!\n");
/* work with the file */
fclose(handle);
return 0; /* all cool */
}
And then you run it and input something like:
C:\cygwin\home\myaccount\main.c
It should print 'Should be good!' (provided the file does exist, you can also test with 'C:\').
At least on Windows 7 with cygwin this is what I get. No need for any escapes as this is handled for you.

Using a C string like a FILE*

I have a C function that reads a stream of characters from a FILE*.
How might I create a FILE* from a string in this situation?
Edit:
I think my original post may have been misleading. I want to create a FILE* from a literal string value, so that the resulting FILE* would behave as though there really was a file somewhere that contains the string without actually creating a file.
The following is what I would like to do:
void parse(FILE* f, Element* result);
int main(int argc, char** argv){
FILE* f = mysteryFunc("hello world!");
Element result;
parse(f,&result);
}
Standard C provides no such facility, but POSIX defines the fmemopen() function that does exactly what you want.
Unfortunately, C's standard library doesn't provide this functionality; but there are a few ways to get around it:
Create a temporary file, write your string to it, then open it for reading. If you've got POSIX, gettempnam will choose a unique name for you
The other option (again for POSIX only) is to fork a new process, whose job will be to write the string to a pipe, while you fdopen the other end to obtain a FILE* for your function.
As #KeithThompson pointed out, fmemopen does exactily what you want, so if you have POSIX, use that. On any other platform, (unless you can find the platform-equivalent), you'll need a temporary file.
Last time I had this kind of problem I actually created a pipe, launched a thread, and used the thread to write the data into the pipe... you would have to look into operating system calls, though.
There are probably other ways, like creating a memory mapped file, but I was looking for something that just worked without a lot of work and research.
EDIT: you can, of course, change the problem to "how do I find a nice temporary filename". Then you could write the data to a file, and read it back in :-)
pid_t pid;
int pipeIDs[2];
if (pipe (pipeIDs)) {
fprintf (stderr, "ERROR, cannot create pipe.\n");
return EXIT_FAILURE;
}
pid = fork ();
if (pid == (pid_t) 0) {
/* Write to PIPE in this THREAD */
FILE * file = fdopen( pipe[1], 'w');
fprintf( file, "Hello world");
return EXIT_SUCCESS;
} else if (pid < (pid_t) 0) {
fprintf (stderr, "ERROR, cannot create thread.\n");
return EXIT_FAILURE;
}
FILE* myFile = fdopen(pipe[0], 'r');
// DONE! You can read the string from myFile
.... .....
Maybe you can change the code a little bit to receive a custom handle.
void parse(my_handle *h, Element *result)
{
// read from handle and process
// call h->read instead of fread
}
and defines the handle like this:
struct my_handle
{
// wrapper for fread or something
int (*read)(struct my_handle *h, char *buffer, int readsize);
// maybe some more methods you need
};
implement your FILE* wrapper
struct my_file_handle
{
struct my_handle base;
FILE *fp;
};
int read_from_file(struct my_handle *h, char *buffer, int readsize)
{
return fread(buffer, 1, readsize, ((my_file_handle*)h)->fp);
}
// function to init the FILE* wrapper
void init_my_file_handle(struct my_file_handle *h, FILE *fp)
{
h->base.read = read_from_file;
h->fp = fp;
}
Now, implement your string reader
struct my_string_handle
{
struct my_handle base;
// string buffer, size, and current position
const char *buffer;
int size;
int position;
};
// string reader
int read_from_string(struct my_handle *h, char *buffer, int readsize)
{
// implement it yourself. It's easy.
}
// create string reader handle
void init_my_string_handle(struct my_string_handle *h, const char *str, int strsize)
{
// i think you know how to init it now.
}
//////////////////////////////////////////////////
And now, you can simply send a handle to your parse function. The function doesn't care where the data comes from, it can even read data from network!
This is an old question, but deserves a better answer.
C has always had the ability to read and write strings using the formatted I/O functions. You just need to keep track of where you are in the string!
Reading a string
To read a string you need the %n format string specifier, which returns the number of bytes read each time we use sscanf(). Here is a simple example with a loop:
#include <stdio.h>
int main(void)
{
const char * s = "2 3 5 7";
int n = 0;
int value;
while (sscanf( s+=n, "%d%n", &value, &n ) == 1)
{
printf( "value = %d\n", value );
}
}
Another way to have written that loop would be:
for (int value, n; sscanf( s, "%d%n", &value, &n ) == 1; s += n)
Whichever floats your boat best.
The loop is not important.
What is important is that we increment the value of s after every read.
Notice how we don’t bother to remember original value of s in this example? If it matters, use a temporary, as we do in our next example.
It is also important that we stop reading when sscanf fails. This is the normal usage for the scanf family of functions.
Writing a string
In this case sprintf() helps us by directly returning the number of bytes written. Here’s a simple example of building a string using several formatted outputs:
#include <stdio.h>
int main(void)
{
char s[100] = {0};
char * p = s;
p += sprintf( p, "%d %s", 3, "three" );
p += sprintf( p, "; " );
p += sprintf( p, "%.2f %s", 3.141592, "pi" );
*p = '\0'; // don’t forget it!
printf( "s = \"%s\"\n", s );
printf( "number of bytes written = %zu = %zu\n", p-s, strlen(s) );
}
The important points:
This time we do not want to clobber s (and in this particular example couldn’t even if we wanted to), so we use a helper p.
We cannot forget to manually add that null-terminator. (Which should make sense, since we are manually building the string.)
BUFFER OVERFLOW IS POSSIBLE
That last point is significant, and a usual concern when building strings in C. As always, whether using strcat() or sprintf(), always make sure you have enough room to append everything you intend to write to your string!
Don’t use %n when writing
We could have used the %n specifier as well, but then we hit a cross-platform issue with MSVC: Microsoft targets %n and the printf() family of functions as a security issue. Whether or not you accept Microsoft’s reasoning you must live with the way things are.
If you are undeterred, you can add a little platform-specific code and use it anyway:
#ifdef _WIN32
_set_printf_count_output( 1 );
#endif
int n;
printf( "Hello%n world!", &n );
True FILE * I/O
Notice that we aren’t touching actual FILE * I/O functions, like fgetc()? If you need that, then you need an actual file.
As mentioned above, use tmpfile() to open a temporary read/write file and use the usual FILE * I/O functions on it. Our read-a-string example could be re-written as:
#include <stdio.h>
int main(void)
{
FILE * f = tmpfile();
if (!f) return 1;
fprintf( f, "2 3 5 7" );
rewind( f );
int value;
while (fscanf( f, "%d", &value ) == 1)
{
printf( "value = %d\n", value );
}
fclose( f );
}
This works just fine. Remember that tmpfile() might not give you an actual file on disk with a filename. You don’t need that anyway. In other words, it may very well be an in-memory buffer provided by the OS... which is kind of what this thread is about anyway, right?
Hopefully these options will give a deeper insight into the C standard I/O functions and their use. Next time you need to read or build a formatted string in parts, you will have a better grasp of the tools already provided for you.

Resources