Creating a File to array function - c

Currently I have the argc, argv and temp pieces placed in to be passed, and when I compile it returns no errors, but when I call the function later in the program and pass it a char array. It returns a stack dump. From what I have learned so far arrays cannot be passed back from a function and that is why I have passed the pointers.
int In2File(int argc, char *argv[], char *temp[] ){
if (argc == 2) { //open file
FILE *user_file;
user_file = fopen(argv[1], "r");
if (user_file == NULL) {
printf("No data was found.\nEnd(Error 1)");
exit(2);
}
else {
int g = 0;//temp counter to load
char c = 0;
while ((c = fgetc(user_file)) != EOF && g <= tmplng - 1) { //Used Fgetc instead of fgets because Fgetc allows me to read
*temp[g] = c; //until the end of the file and read each character. Even if there is an \n character.
g++; // The second g < tmplng-1 is used to make sure that the \0 can be placed into the array.
}
printf("%s\n", temp);
fclose(user_file);//Closed the txt file that was loaded by user in args(1)
printf("\nFile Loaded.\n");
}
}
else if (argc > 2) { // Will exit if arguments are greater than 2.
printf("Format: %s 'filename'", argv[0]);
exit(1);
}
else {
printf("File not provided. Enter information:\n");//If the user doesnt provide a file allow manual input.
fgets(*temp, tmplng, stdin);
}
}
In2File(argc,argv,temp);
Anyone have an idea as to where I went wrong with this function? I read a few similar posts but they were for C++ and Python. Which I havent learned C++ as yet and python is different to this beast called C.
Edit:
const int tmplng = 1000; //The only variables needed
char temp[tmplng]; //
char temp2[tmplng]; //
printf("Starting....\n"); //Used for testing debugging.

The third parameter to the function doesn't match what the function is expecting. You're passing in a char [] (which decays to a char *), while the function expects char *[] (equivalently char ** as a function parameter).
The definition of the third parameter doesn't match how you intend to use it, which is as a character array.
Get rid of the extra level of indirection on the parameter, and adjust the function accordingly.
int In2File(int argc, char *argv[], char temp[] ){
...
while ((c = fgetc(user_file)) != EOF && g <= tmplng - 1) {
temp[g] = c;
g++;
}

This declaration of temp:
char temp[tmplng];
... does not go with this call ...
In2File(argc,argv,temp);
... to this function ...
int In2File(int argc, char *argv[], char *temp[] );
The function expects its third argument to be a pointer to a pointer to char *, but what it receives is a pointer to char. When it then tries to assign the character read ...
*temp[g] = c;
That attempts to interpret the gth element of temp (itself interpreted as a char **) as a char *, to dereference that pointer, and to assign c to that location. That is highly unlikely to be a valid memory access.
It looks like you want to declare the third argument of In2File as either char *temp or char temp[], which are equivalent, and to write to it as temp[g] = c.
ALSO, as an aside, note that your
printf("%s\n", temp);
is problematic because you do not ensure that temp is null-terminated. And inasmuch as you support reading up to the very last byte, you don't have room to terminate it.

As numerous folks are pointing out in the comments, your code would be much easier to understand if you employed "early exit" for your error conditions.
It would also be a better separation of concerns to have main deal with the program arguments and let In2File just deal with a file pointer.
int main( int argc, char *argv[] ) {
FILE *fp;
if( argc == 2 ) {
fp = fopen(argv[1], "r");
if ( fp == NULL ) {
fprintf(stderr, "Couldn't open %s: %s", argv[1], strerror(errno));
exit(2);
}
}
else if( argc > 2 ) {
fprintf(stderr, "Usage: %s <optional filename>", argv[0]);
exit(1);
}
else {
fp = stdin;
}
char *lines = In2File(fp);
printf("%s\n", lines);
free(lines);
}
Note that stdin is a file pointer just like anything you opened with fopen. I've also used strerror and errno to provide the reason the file didn't open.
Now In2File just takes a file pointer. Also note that I have it returning the content, not passing in a preallocated array. This is because when reading from input you never know how much input you're going to get. You either must stop reading before you run out of space, or allocate more memory. You can't reallocate stack memory, so it's best to let In2File control allocating memory. It also avoids having to pass around a global variable.
Once that's done, In2File becomes much simpler.
static char *In2File( FILE *fp ){
size_t read_size = 1024;
/* Don't forget space for null */
char *content = calloc( read_size + 1, sizeof(char) );
fread( content, sizeof(char), read_size, fp );
return content;
}
Rather than step through a character at a time, I've used fread to read a block from the file not larger than the amount I've allocated. Memory reallocation is a topic for another time.

Related

Program containing fscanf fails to compile

As someone who is pretty new to C, I'm still trying to wrap my head around the massive amount of functions.
One in particular is giving me a lot of problems.
I'm trying to use fscanf but I keep getting a strange error:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *dict = fopen(argv[1], "r");
// skim through every word in a dictionary
while (fscanf(dict, "%s", word) != EOF)
{
// iterate through every chracter in the word
for (int i = 0, int j = strlen(word); i < j; i++)
{
printf("%c", word[i]);
}
printf("\n");
}
return 1;
}
I'm trying to create a loop that skims through every single word in a file that is just a series of words.
I'm assuming that the code I've written does just that, storing each word in a string called "word".
Unfortunately, I keep getting this error:
error: format specifies type 'char *' but the argument has type
'<dependent type>' [-Werror,-Wformat]
error: use of undeclared identifier 'word'
Even if I try to initialize a char * variable "word", some other error just keeps popping up. Maybe I don't fully understand how fscanf works, could anyone provide some clearance?
How can I make the above program compile?
Because you are using C, this answer does not care about infamous "buffer overflow error" but please keep in mind that.
You need to declare 'word' variable before you use 'fscanf'. It looks like...
const size_t MAX_WORD_LEN=256;
char word[MAX_WORD_LEN];
then, you can.
while (fscanf(file, "%s", word) != EOF)
{
}
Here, the maximum 'word' length is 255 characters (without terminating NULL character). So in your file one single word length cannot be over 255 characters (in most cases, it should be fine).
The whole program most likely be as follow:
#include <stdio.h>
const char* const usage_msg = "Usage: program_name input-file\nInput file contains words in English or some languages. \n";
const char* const fopen_err = "\nUnable to open file: ";
const size_t MAX_WORD_LEN = 256;
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "%s", usage_msg);
return -1;
}
else {
/* if argv[1] contains something that is not supposed to be, it will be infamous buffer overflow. */
/* any validation on argv[1] is welcomed here */
FILE *pf = fopen(argv[1], "r");
char word[MAX_WORD_LEN];
if (pf == NULL) {
fprintf(stderr, "%s%s", fopen_err, argv[1]);
return -2;
}
/* overflow can be avoided by specifying width 255 to %s */
while (fscanf(pf, "%255s", word) != EOF) {
printf("%s\n", word);
}
}
return 0;
}

getting a "free(): invalid pointer" in c

i am new at writing in c,
my program is for parsing a txt file and save the first word in a struct named File.
for exmple:
for a txt file containing:
file1: file2,file3
file66: file7
i would like to save file1 and file2 in a struct
when
file1->name=file1
fiel1->relation=[file2,file3]
but this just for a bit explantion of the program.
the problem is:
because i dont know the size of the array of chars that would represent the name i were trying to use dynamic memory using malloc and free
in the process i am using strtok for the parsing part
the problem start in the 4 last lines (marked in a comment)
and i keep getting the error "free(): invalid pointer: 0x00007ffe6accfdd0 ***"
(i looked in the website for answers but i because the lack of understanding in pointer it was hard for me to get the idea of the problem).
someone can explain me why?
thank you from advance
typedef struct File
{
char *name;
int *relation;
}File;
char *error = BAD_FILE_MSG;
char buffer[MAX_LEN_SIZE];//the file buffer
if (argc != RIGHT_NUM_OF_PARAM) {
fprintf(stderr, UNVALID_PARAMETER_MSG);
return BAD_RET_VAL;
}
char *fileName = argv[1];
FILE *fp = fopen(argv[1], "r"); /* "r" = open for reading */
if (fp == NULL)
{
fprintf(stderr, "%s %s", error, fileName);
return BAD_RET_VAL;
}
if (ferror(fp) != 0)
{
fprintf(stderr, "%s %s", error, fileName);
return BAD_RET_VAL;
}
//int line = 0;//todo magic
while (fgets (buffer, sizeof(buffer), fp))
{
/**
* saving all the line in a char[]
*/
//give memory to an array in file
char *token;
token = strtok (buffer, SIGN_FOR_SEPARATE);
int marker = 0;//todo fix
//creating a struct of a file
File* fileStruct = (File*)malloc(sizeof(File));
//creating a dynamic array of ints for relation
fileStruct->relation = (int *)malloc(100 * sizeof(int));//todo free your mind
char file[100];
while (token != NULL)
{
if (marker == 0)
{
char* pointer = fileStruct->name;
size_t size = strlen(token);
//creating a dynamic array of chars for name
fileStruct->name = (char *)malloc(size + 1);
fileStruct->name = token;//**getting the error**
free(pointer);
marker++;
}
I'm just going to focus on what is causing your error (I didn't read most of your code).
Pointers are one of the concepts in C that take a long time to learn, and you have a long way to go.
A pointer is just an address in memory, nothing else. You can think of *pointer as being a function call that says, "take the number stored in pointer, go out to memory, and return the value that you find at the address corresponding to that number".
When you say:
char* pointer = fileStruct->name;
You aren't connecting those two variables in any way. It would be like saying:
int foo = 3;
int bar = foo;
For now they have the same value, but if you change foo, bar doesn't change.
In your code, you aren't actually using pointer anywhere, so you can just gt rid of it and call free(fileStruct->name) when you are done using it.
That being said, you need more practice/reading/learning about how pointers work. If you are just starting to program in general, you might want to avoid pointers all together until you are are comfortable with the basics.

Storing line by line from text file into char array using char pointer

Hello I writing a program to execute commands from the text file. The code below is used to store line by line into char array first.
So I expect it to do something like
args[0]= The first line of text file
args[1]= The second line of text file
... and so on
In my code, all of the arrays would be covered by the last array. And I can't figure out why.
Can anyone help me fix this and tell me why my code does that. Also I need to keep char *args[]. cos I would use it with execvp() later.
int main(int argc, const char * av[]) {
FILE *fp;
fp = fopen(av[1],"r");
int n_lines=0;
char in[100],*args[16];
int size=sizeof(in);
while(fgets(in, size, fp)!=NULL){
args[n_lines] = in;
printf("Args[0] is %s\n",args[0]);
n_lines++;
}
printf("Now Args[0] is %s\n",args[0]);
}
Output
zacks-MacBook-Pro:prac2 zack$ ./a.out test
Args[0] is ./addone
Args[0] is ./add
Now Args[0] is ./add
int n_lines=0;
char in[100],*args[16];
int size=sizeof(in);
while(fgets(in, size, fp)!=NULL){
args[n_lines] = in;
printf("Args[0] is %s\n",args[0]);
n_lines++;
}
The value of in is overwritten on each iteration, you need to reserve space (using malloc->strcpy or strdup if available):
char in[100], *args[16];
while (fgets(in, sizeof in, fp) != NULL) {
args[n_lines] = strdup(in);
...
n_lines++;
}
Or use a 2D array (an adjust of sizeof is required in fgets):
char in[16][100];
while (fgets(in[n_lines], sizeof in[0], fp) != NULL) {
...
n_lines++;
}
And as pointed out by #MichaelWalz in comments: you'll run into problems if your file has more then 16 lines.
Change to
while (fgets(in[n_lines], sizeof in[0], fp) != NULL) {
...
if (++n_lines == (sizeof in / sizeof in[0])) break;
}

C - Error with function parameters when it is being called

When I am trying to call a function, the parameters that I have tried so far have just resulted in the terminal saying that 'too few arguments to function' and what I can tell that what it wants written there is the parameters from when it was declared. I have read a few different documents about this and what the calling parameters are and the different between calling by value or by reference but I am still not able to figure out the problem.
Below is the main section of code that has the call functions in it along with some variables.
int main(int argc, char *argv[])
{//main()
char *listwords;
processfile(listwords); //<- this is the line that is causing the problem
WordList mylist;
initialiselist(&mylist);
addword(&mylist, createfillednode(listwords));
printlist(&mylist);
}//main()
Below here is the processfile() function:
//process the file
void processfile(WordList *wordList, int argc, char *argv[])
{//process file
//file pointer
FILE *f;
//open the file
f = fopen(argv[1], "r");
//check it opened correctly
if(f == NULL)
{//if statement
printf("cannot read file\n");
}//if statement
fseek(f, 0, SEEK_END);
//declare variables
char *listwords;
long size = ftell(f);
char *token;
//seek beginning of file
fseek(f, 0, SEEK_SET);
//set the size of array to the file size
listwords = (char*)malloc(size+1);
listwords[size] = '\0';
//reads the data from the file
fread(listwords, size, 1, f);
int i;
for(i=0; (token = strsep(&listwords, " ")); i++)
{//for loop replace certain characters with spaces
if (token != ".")
{
//pointer from the token to the dictionary
wordList->token;
}else if (wordList->token != "!")
{
wordList->token;
}else if (token != "?")
{
wordList->token;
}else if (token != "\"")
{
wordList->token;
}else if (token != ","){
wordList->token;
}else
{
wordList->token;
}
//increment token to the next word
token++;
}//for loop replace certain characters with spaces
fclose(f);
return;
}//process file
Thanks.
You've declared processfile to take three arguments.
void processfile(WordList *wordList, int argc, char *argv[])
But you're only giving it one.
processfile(listwords);
And it's also the wrong type. It should be a WordList, but instead it's a string (char *).
char *listwords;
In C, you have to give a function exactly the right number of arguments and of the right type (C can do some type casting, but it's strictly defined and usually about numbers).
With one exception. Variadic arguments let you pass an undefined number of arguments. This is how functions like printf work. In general you should avoid variadic functions as much as possible, they add complexity and defeat type checking.
In your case, processfile only needs two arguments: a list of words, and a filename to open. argc is never used, and knowing that the filename is coming from argv[1] puts an unnecessary restriction on the function.
void processfile(WordList *wordList, char *filename)
Then it can be called with...
WordList *wordList = ...generate the wordlist somehow...
processfile(wordList, argv[1]);
The processfile() function takes three arguments according to your definition:
void processfile(WordList *wordList, int argc, char *argv[])
However, in your main() function you're only passing a single argument:
processfile(listwords)
To make this work like you'd want it to, you'd have to pass all three arguments; your listwords, as well as the counter and vector for arguments:
processfile(listwords, argc, argv)
However, in general, this is usually not a great idea programatically speaking as far as I know. A function should typically take some sort of specialized input and return a relevant value, and not the input from the command line - that should be parsed far ahead of the function actually being called. Check out getopt or argparse to process arguments correctly, determine what parameters you really want to pass to processfile() and pass only the arguments that would be relevant to the file in which you're attempting to process. From reading your code, probably just the file descriptor, which should be opened in main() after some argument-parsing error correction:
void processfile(WordList *wordList, FILE *f)
{
fseek(f, 0, SEEK_END);
//declare variables
char *listwords;
long size = ftell(f);
char *token;
fseek(f, 0, SEEK_SET);
listwords = (char*)malloc(size+1);
listwords[size] = '\0';
fread(listwords, size, 1, f);
- - - - - - - - - 8< - - - - - - - - -
This way, you can open your file elsewhere, and re-use processfile() on any open file called in any context, making it a more robust function.

Why am I segfaulting?

I'm very new to C, I am attempting to read the contents of one file character by character and output them to the stream. But even with my fopen() command commented out I receive segfault (core dumped).
I must run a command: ./a.out < testWords.in > myOut.txt to execute my file properly.
Here is what I have so far:
#include <stdio.h>
void main(char *fileName[])
{
printf("filename is %s.\n",fileName[0]);
//Get file based on a string inputed
FILE *fp=fopen(fileName[0],"r"); //Fetches our file as read only
char ch;
int lineCount = 0;
int wordCount = 0;
int charCount = 0;
//Failed to find/open file. NULL character.
if (fp == 0) printf("Woops! Couldn't open file!\n");
//While not at end of file, grab next char.
else while( (ch=fgetc(fp)) != EOF)
{
if (ch == '\n') //on newline
{
//Prints (charCount,wordCount)\n lineCount:
printf("(%d,%d)%c%d:",charCount,wordCount,ch,lineCount);
charCount = 0;
lineCount += 1;
}
else printf("%c",ch); //mirrors char.
}
fclose(fp); //Closes file (gotta be tidy!)
}
You can't just invent a way to call main. You need to use one of the standard ways, like this:
int main(int argc, char *argv[])
{
if (argc < 2) {
fprintf(stderr, "Missing filename\n");
return -1;
}
FILE *fp = fopen(argv[1], "r");
// ...
}
And note that argv[0] contains the program name (if available; if not it contains an empty string).
Your program segfaulted because you received the int argc argument into your char *filename[] parameter. If you ran the program with a single command line parameter, the value passed in as the first argument would have been 2, which is not a valid pointer value. The expression filename[0] dereferences that address and causes a segfault.
Any time you get a segfault in C, you should smell a bad pointer or address in an argument list. In this particular case., the signature of main is always int main(int argc, char** argv). Yours isn't.
What you want is
int main(int argc, char ** argv) {
...
FILE * fp = fopen(argv[1]); // Quiz: why argv[1]? What's argv[0]?
You're getting away with it in the compiler because, basically, luck.
I also notice in your example call, there's actually no argument in the argument list, because you're using redirection.
Use:
int main(int argc, char* argv[])
And use argv[1] as fileName.
Main function must receive always that two parameters.

Resources