I have written this function which reads a file, compares a string to each line and if the line contains the string, it return it.
char* FileSearch2 (char* File_Name , char* String) {
FILE * T = fopen(File_Name, "rt");
char* Line = (char*) malloc (sizeof(char*) * 1024);
strcat(String,"\t");
if (T) {
while (fgets(Line,1024,T)) {
if (strstr(Line, String) != NULL) {
fclose(T);
return Line;
}
}
}
fclose(T);
return "0";
}
The problem is that, the second time I run this function it always returns "0".
For example
char* FirstRun = FileSearch2 ("File.txt", Value); // Assuming the value is "Hello", it returns the line
Now
char* SecondtRun = FileSearch2 ("File.txt", Value); // Assuming the value is "Hello", it returns "0"
I would like to know what exactly I am doing wrong.
Your code has a few serious errors.
For instance, your malloc() logic is all wrong.
You seem to want to allocate room for a line of 1024 characters, but you do this:
char* Line = (char*) malloc (sizeof(char*) * 128);
it should just be:
char *Line = malloc(1024);
Don't cast the return value of malloc() in C, and the business with sizeof(char *) is just plain wrong and confused.
Then you fail to free() the line if the line is not found, which is another problem.
Your code also changes the caller's data by calling strcat(), which means that if you call it with e.g. a string literal (as your example implies) it's totally undefined behavior what's going to happen. You should re-think that part with the '\t'-appending, it's not the best way to do it.
Well, I expect it's because you function FileSearch modifies String by appending a tab character to it. So the second time through, you're looking for a string with two tabs at the end.
You should make a copy of String, append the tab to the copy, and free the copy at the end.
char *copy = strdup(String);
strcat(copy,"\t");
if (T) {
while (fgets(Line,1024,T)) {
if (strstr(Line, copy) != NULL) {
fclose(T);
free(copy);
return Line;
And, as #unwind says, you need to free Line (and copy) if the string isn't found, too.
Related
I need to create 2 separate functions readLine() and readLines() in C. The first one has to return the pointer to the input string and the second one should return the array of pointers of input strings. readLines() is terminated with a new line character. I am getting some errors, probably something with memory, sometimes it works, sometimes it doesn't. Here is the code:
char* readLine() {
char pom[1000];
gets(pom);
char* s = (char*)malloc(sizeof(char) * strlen(pom));
strcpy(s, pom);
return s;
}
And here is readLines()
char** readLines() {
char** lines = (char**)malloc(sizeof(char*));
int i = 0;
do {
char pom[1000];
gets(pom);
lines[i] = (char*)malloc(sizeof(char) * strlen(pom));
strcpy(lines[i], pom);
i++;
} while (strlen(lines[i - 1]) != 0);
return lines;
}
In the main, I call these functions as
char* p = readLine();
char** lines = readLines();
When allocating memory for a string using malloc, you should allocate enough memory for the entire string, including the terminating null character.
In your case, strcpy will cause a buffer overflow, because the destination buffer isn't large enough.
You should change the line
char* s = (char*)malloc(sizeof(char) * strlen(pom));
to
char* s = (char*)malloc( sizeof(char) * (strlen(pom)+1) );
and change the line
lines[i] = (char*)malloc(sizeof(char) * strlen(pom));
to
lines[i] = (char*)malloc( sizeof(char) * (strlen(pom)+1) );
Also, the line
char** lines = (char**)malloc(sizeof(char*));
is wrong, as it only allocates enough memory for a single pointer. You need one pointer per line. Unfortunately, you don't know in advance how many lines there will be, so you also don't know how many pointers you will need. However, you can resize the buffer as required, by using the function realloc.
Although it is unrelated to your problem, it is worth noting that the function gets has been removed from the ISO C standard and should no longer be used. It is recommended to use fgets instead. See this question for further information: Why is the gets function so dangerous that it should not be used?
Also, in C, it is not necessary to cast the return value of malloc. This is only necessary in C++. See this question for further information: Do I cast the result of malloc?
In your code, you first increment i using the statement i++; and then you reconstruct the previous value of i by subtracting 1:
while (strlen(lines[i - 1]) != 0);
This is unnecessarily cumbersome. It would be better to write
while (strlen(lines[i++]) != 0);
and remove the line i++;. That way, you no longer have to subtract by 1.
I have a dynamic array that holds a string containing '\n' characters, so this string is made up of multiple lines. I'm trying to extract the lines and put them all into a 2D char array and I'm getting segmentation errors.
Here's my code:
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
input_lines = extractInput(MAX_LINE_LEN, input_file);
char inputLines_counted[lineCount_input][MAX_LINE_LEN];
char *t = strtok(input_lines, "\n");
for(i = 0; i < lineCount_input; i++) {
strcpy(inputLines_counted[i], t);
// printf("%s\n", inputLines_counted[i]);
t = strtok(NULL, "\n");
}
Upon creating the dynamic array, I use the extractInput(MAX_LINE_LEN, input_file) function to populate the input_lines array with a string containing multiple lines.
Here's the extract function:
char *extractInput(int len, FILE *file) {
char tmp[len];
char *pos;
char *input_lines = malloc(len*sizeof(char));
char *lines;
while(fgets(tmp, len, file)) {
// if((pos = strchr(tmp, '\n')) != NULL) {
// *pos = ' ';
// }
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
strcat(input_lines, tmp);
}
return input_lines;
}
Why am I getting segfaults here?
The function call
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
takes your current allocated memory block and expands it, if it can. you should check the return value of realloc, it may fail.
btw when you allocate memory in C, you always need to have space for the ending \0.
see what happens with this file
hello\n
world\n
The first fgets reads in hello\n into tmp.
you now do realloc even though it is unnecessary, input_lines is already pointing to a buffer that could hold the string
char *input_lines = malloc(MAX_LINE_LEN*sizeof(char));
now with your realloc
input_lines = realloc(input_lines, (strlen(input_lines) + len)*sizeof(char));
you do strlen(input_lines) + len so you make the buffer strlen("hello\n") + len long.
but the important thing you need to notice is the following line
strcat(input_lines, tmp);
you have not initialized the memory that input_lines is pointing to, it can contain anything even \0's so your strcat could potentially put the string anywhere in the buffer and cause the error you describe.
Either do a memset or use calloc when you allocate the buffer.
If you use realloc you should keep track of the total size that you have allocated and how much you are using of it, before you copy into the buffer check if there is enough room. If not, add a certain number of bytes to the buffer.
I also noticed you read from the file line by line, then you concatenated the lines together to later use strtok to divide them again. It would be more efficient to return an array of lines.
I have 2D array I want populate then compare to literal
below is compare code, i try different things with no success
char** list;
load(list);
if(strcmp(list[0], "aasdf"))
{
printf("win\n");
}
the above segfaults on strcmp
load function
void load(char **list)
{
int MAX_NUM_LINES = 1000;
FILE *fp;
list = malloc(MAX_NUM_LINES*sizeof(char*));
fp = fopen("list", "r");
line_ct = 0;
char line[256];
while ( fgets(line, 256, fp) != NULL )
{
int len = strlen(line);
list[line_ct] = malloc(len * sizeof(char));
strcpy(list[line_ct], line);
line_ct++;
if(line_ct == MAX_NUM_LINES)
{
break;
}
}
fclose(fp);
}
any ideas on why is segfault?
also i try before strcmp
printf("Line: %s\n", *list[0]);
it segfault to
when you come back from load the var list is not set, so when you do
if(strcmp(list[0], "aasdf"))
you have an undefined behavior using list (typically a crash)
The first solution is use an output var
you need to change
load(list);
by
load(&list);
and you need to change the type of list and dereference it in load, so :
void load(char ***list)
{
*list = malloc(MAX_NUM_LINES*sizeof(char*));
...
(*list)[line_ct] = malloc((len + 1) * sizeof(char));
strcpy((*list)[line_ct], line);
I also added 1 to len to have place for the ending null character.
(edit) The use of a *** is not very common, as suggested by #user3629249 in a remark you can look at Triple pointers in C: is it a matter of style? reading carefully the answers.
The second solution is to return the allocated array :
char** list = load();
with
char ** load()
{
char **list;
...
return list;
also adding 1 to len when you allocate each line
Out of that if you read more than MAX_NUM_LINES lines you write out of the array again with an undefined behavior, and the caller of load does not know how much lines you read.
To avoid that you can first initialize list with malloc(0) then use realloc to increase the size of list each time you read a line, that allows to allocate the right size. To indicate the size to the caller you can use an additional output var or to allocate one entry more to place NULL in the last entry (all depends on how you use the read array in the code calling load)
I'm getting a core dump that I have no clue how to solve. I have searched other questions and googled my problem but I just can't figure out how to solve this...
Here is the code:
const char checkExtension(const char *filename)
{
const char *point = filename;
const char *newName = malloc(sizeof(filename-5));
if((point = strrchr(filename,'.palz')) != NULL )
{
if(strstr(point,".palz") == 0)
{
strncpy(newName, filename, strlen(filename)-5);
printf("%s\n",newName ); // the name shows correctly
return newName; // Segmentation fault (core dumped)
}
}
return point;
}
The function was called char checkExtensions(const char *filename). I added the const due the solutions that I have found online but so far I haven't been able to make it work...
Thank you in advance for the help!
You have many problems with your code. Here are some of them:
Your function returns char which is a single character. You need to return a pointer to an array of characters, a C string.
You don't allocate the right amount of memory. You use sizeof() on a pointer which yields the size of a pointer.
You make it impossible for the caller to know whether or not to deallocate memory. Sometimes you heap allocate, sometimes not. Your approach will leak.
You pass '.palz', which is a character literal, to strrchr which expects a single char. What you mean to pass is '.'.
A better approach is to let the caller allocate the memory. Here is a complete program that shows how:
#include <string.h>
#include <stdio.h>
void GetNewFileName(const char *fileName, char *newFileName)
{
const char *dot = strrchr(fileName, '.');
if (dot)
{
if (strcmp(dot, ".palz") == 0)
{
size_t len = dot - fileName;
memcpy(newFileName, fileName, len);
newFileName[len] = 0;
return;
}
}
size_t len = strlen(fileName);
memcpy(newFileName, fileName, len);
newFileName[len] = 0;
return;
}
int main(void)
{
char fileName[256];
char newFileName[256];
strcpy(fileName, "foo.bar");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
strcpy(fileName, "foo.bar.palz");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
strcpy(fileName, "foo.bar.palz.txt");
GetNewFileName(fileName, newFileName);
printf("%s %s\n", fileName, newFileName);
return 0;
}
Output
foo.bar foo.bar
foo.bar.palz foo.bar
foo.bar.palz.txt foo.bar.palz.txt
Note that strcmp compares sensitive to letter case. On Windows file names are insensitive to case. I will leave that issue for you to deal with.
By letting the caller allocate memory you allow them to chose where the memory is allocated. They can use a local stack allocated buffer if they like. And it's easy for the caller to allocate the memory because the new file name is never longer than the original file name.
This is most probably your problem:
const char *newName = malloc(sizeof(filename-5));
First, filename is of type const char *, which means that (filename - 5) is also of this type. Thus, sizeof(filename - 5) will always return the size of the pointer datatype of your architecture (4 for x32, 8 for x64).
So, depending on your architecture, you are calling either malloc(4) or malloc(8).
The rest of the code doesn't even compile and it has serious string manipulation issues, so it's hard to tell what you were aiming at. I suppose the strncpy() was copying too much data into newName buffer, which caused buffer overflow.
If your goal was to extract the filename from a path, then you should probably just use char *basename(char *path) for that.
Several pretty major problems with your code. Making it up as I type, so it may not fix everything first time right away. Bear with me.
You need to return a char *, not a char.
const char checkExtension(const char *filename)
{
const char *point = filename;
You malloc memory but the instruction flow does not guarantee it will be freed or returned.
sizeof(filename) should be strlen(filename), minus 5 (sans extension) but +1 (with terminating 0).
const char *newName = malloc(sizeof(filename-5));
strrchr searches for a single character. Some compilers allow "multibyte character constants", but they expect something like 2 -- not five. Since you know the length and start of the string, use strcmp. (First ensure there are at least 5 characters. If not, no use in testing anyway.)
if((point = strrchr(filename,'.palz')) != NULL ) {
Uh, strstr searches for a string inside a string and returns 0 if not found (actually NULL). This contradicts your earlier test. Remove it.
if(strstr(point,".palz") == 0)
{
strncpy copies n characters, but famously (and documented) does not add the terminating 0 if it did not get copied. You will have to this yourself.
.. This is actually where the malloc line should appear, right before using and returning it.
strncpy(newName, filename, strlen(filename)-5);
printf("%s\n",newName ); // the name shows correctly
return newName; // Segmentation fault (core dumped)
}
}
You return the original string here. How do you know you need to free it, then? If you overwrote a previous char * its memory will be lost. Better to return a duplicate of the original string (so it can always be freed), or, as I'd prefer, return NULL to indicate "no further action needed" to the calling routine.
return point;
}
Hope I did not forget anything.
There are several problems with your code:
Wrong return type:
const char checkExtension(const char *filename){
You need to return a pointer (const char *), not a single character.
Not enough memory:
const char checkExtension(const char *filename){
const char *newName = malloc(sizeof(filename-5));
You are allocating the size of a pointer (char *), which is typically 4 or 8. You need to call strlen() to find out the size of the string:
Multibyte character:
if((point = strrchr(filename,'.palz')) != NULL ) {
'.palz' is a multibyte character literal. While this is allowed in C, its value is implementation-defined and might not do what you expect. String literals use double quotes (".palz").
No terminating zero:
strncpy(newName, filename, strlen(filename)-5);
Note that strncpy() doesn't necessarily null-terminate the target string. It write at most strlen(filename)-5 characters. If the source string contains more characters (as in your case), it will not write a terminating zero.
I'm not sure what exactly you're trying to do. Perhaps something like this:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const char *checkExtension(const char *filename)
{
int len = strlen (filename)-5;
char *newName = NULL; /* return NULL on allocation failure. */
if (len > 0 && !strcmp (filename+len, ".palz")) {
newName = malloc (len+1);
if (newName) {
memcpy (newName, filename, len);
newName[len] = 0;
}
}
return newName;
}
int main (int ac, char **av)
{
if (ac > 1) {
const char *p = checkExtension (av[1]);
puts (p ? p : "NULL");
} else {
puts ("?");
}
return 0;
}
Multiple errors here. You have not said what you are trying to achieve, that has to be implied from the code. You have declared point and newName as const, yet reassigned with a value. You have tested strstr() == 0 when it should be strstr() == NULL. You have called strrchr(filename,'.palz') but sent a string instead of a char. Then you have returned the local variable point which goes out of scope before you get a chance to use it, because it was not declared as static. So it's irrelevant whether you returned a char or a char pointer.
char *checkExtension(const char *filename) {
// if filename has extension .palz return a pointer to
// the filename stripped of extension or return NULL
char *point;
static char newName[512];
strncpy(newName, filename, 512);
if ((point = strstr(newName, ".palz")) != NULL ) {
if (strlen (point) == 5) {
*point = 0; // string terminator
// printf("%s\n",newName ); // use only for debugging
return newName;
}
}
return NULL;
}
Alternatively provide a string the function can modify -
char *checkExtension(const char *filename, char *newName) { ... }
Alternatively provide a filename the function can modify -
char *checkExtension(char *filename) {
char *point;
if ((point = strstr(filename, ".palz")) != NULL ) {
if (strlen (point) == 5) {
*point = 0; // string terminator
return filename;
}
}
return NULL;
}
EDIT Thanks Joachim. Used the function signature as you pointed out, and passed the address of my strings in. Inside the function, I did the realloc() using *currBuffer, and placed the values into the string using (*currBuffer)[lenBuffer] .... :)
Passing the value of the pointer by-value was working fine until realloc() decided it needed to assign a different memory space. I must have only checked the pointer's address on entry to the function and not noticed that it changed later.
.........................................................
My program gets user input from stdin, and then parses it into tokens.
As I read each character entered, I make a call addChrToLine(userInput, charCount-1, (char) inputChr); to void addChrToLine (char *currBuffer, int lenBuffer, char inputChr) where I add the new character to the string.
Later, when I'm parsing the input, I using the same addChrToLine function while I build the parsed string. However, when the parser calls the function, it gives an error on the 25th character.
My code to read user input:
char * userInput = malloc(sizeof(char));
int charCount = 0;
LOOP
if (!(inputChr == LF)) { // ignore the LF character as it denotes end of input line
charCount++;
addChrToLine(userInput, charCount-1, (char) inputChr);
continue;
}
My code to add the char to the current line:
void addChrToLine (char *currBuffer, int lenBuffer, char inputChr) {
currBuffer = realloc(currBuffer, sizeof(char) * (lenBuffer +2));
if (currBuffer == NULL) {
perror(NULL);
exit(ENOMEM);
}
currBuffer[lenBuffer] = (char) inputChr;
currBuffer[lenBuffer+1] = (char) '\0';
}
My code where I parse the input:
char * parsedCmd = malloc(sizeof(char));
int ndxOutput = 0;
Switch statement inside a loop to handle variety of cases
char currChr = command[ndxInput];
if (addChr) {
ndxOutput++;
addChrToLine2(parsedCmd,ndxOutput-1,currChr);
// parsedCmd = realloc(parsedCmd, sizeof(char) * (ndxOutput+2));
// parsedCmd[ndxOutput] = command[ndxInput];
// parsedCmd[ndxOutput+1] = '\0';
addChr = FALSE;
}
For input = alphabet, eg "abcde...yz", the user input string is read correctly and everything is as expected on entry to the parsing stage.
While parsing "a" to "w", the char * pointer parsedCmd correctly shows the contents. When I pass "x" to addChrToLine, it correctly places the character and adjusts the position of the null. BUT on return to the parser's next line, parsedCmd shows a null string.
The memory location of the pointer is the same during addChrToLine and after the call as it was before (so realloc() hasn't moved my data). However, when I look at the memory, the first 8 characters of the string are now 0x00 and the the string is intact inclusively after "i", all the way to "w" (no "x").
When the code tries to add "y" to parsedCmd, the realloc() statement issues an error double free or corruption (fasttop): 0x0000000000605160 ***
I'm a bit puzzled why this is happening.
And what puzzles me even more is that if I comment out the call to addChrToLine and uncomment the next three lines (which do the same thing), there is no error and the parser finishes fine.
Clearly I'm doing something wrong with my pointers or memory allocations, but I can't see what's wrong.
Can someone please help me understand what's going on here?
The error is that C passes arguments by value, meaning they are copied. So when you assign to currBuffer in the function you only assign (and modify) the local copy. When the function returns, the old userInput is still unchanged.
To fix this you have to pass the pointer by reference which is done with pointers in C, that is you have to pass a pointer to the pointer:
void addChrToLine (char **currBuffer, int lenBuffer, char inputChr);
...
addChrToLine(&userInput, charCount-1, (char) inputChr);
Another solution is to return the new pointer:
char *addChrToLine (char *currBuffer, int lenBuffer, char inputChr)
{
...
return currBuffer;
}
...
userInput = addChrToLine(userInput, charCount-1, (char) inputChr);