In a separate .txt file, I have: de4 dw9 ds8 g8,7 m3,4 p2,2
The function below cuts the string up into an array.
The output of the array is this:
de4 / w9 / s8 / g8,7 / m3,4 / p2,2 / (null)
As you can see, the "w9" and "s8" are missing the 'd'. I cannot seem to figure out why. Here is my function:
void parseFile(char ** argv)
{
FILE * textFile;
char * string;
char ** lineToken;
int i;
textFile = fopen(argv[1], "r");
lineToken = malloc(sizeof(char *) + 1);
string = malloc(sizeof(char *) + MAX_CHAR);
while(fgets(string, MAX_CHAR, textFile) != NULL)
{
lineToken[0] = strtok(string, " "); /* Put first element in lineToken[0] */
for(i = 1; string != NULL; i++)
{
lineToken = realloc(lineToken, (sizeof(char *) * (i + 1))); /* Realloc since total number of elements is unknown */
if(lineToken == NULL) /* Check to see if reallocing caused an error */
{
move(0,0);
printw("Error reallocing. Press any key to close program.");
refresh();
getch();
exit(0);
endwin();
}
lineToken[i] = strtok(NULL, " "); /* Put new strtok'd string into lineToken[i] */
system("clear");
move(0,0);
printw("%s", lineToken[i]);
refresh();
getch();
}
}
for(i = 0; lineToken[i] != NULL; i++)
{
system("clear");
move(0,0);
printw("%s", lineToken[i]);
refresh();
getch();
}
fclose(textFile);
free(string);
free(lineToken);
}
There are two pretty obvious problems:
The first is the condition for the inner loop when reading from the file, string != NULL. unless the allocation of string failed initially this condition will always be true. You need to check the return of strtok instead: lineToken[i - 1] != NULL.
The second problem is that you only have a single "array" for all lines you read. So if you have more than a single line in the file you overwrite all tokens you read from the first line.
A third problem: If the reallocation fails, you exit the program. However, you have code after that exit call, and the exit function does not return so the call to endwin will not happen in that case.
Related
The task was to read the first 20 lines from a specific file and format them to use only specific parts, the next step was to store those formatted strings in a dynamic array (char ** str | a pointer to a pointer), send it to a function and print it out with said function
Here is the main code:
int main(int argc, char* argv[]){
FILE* file = fopen("./passwd.txt", "r"); // open file
if (!file)
{
perror("Error opening file");
return 1;
}
char line [MAXCHARS];
int counter = 0;
char ** str;
str = malloc(20 * sizeof (char*));
while (fgets(line, MAXCHARS, file) && counter < 20) {
char * position;
if ((position = strchr(line,':'))){
char * end_char;
*position = 0; //setting here the value 0, will terminate the string line (first column)
if((position = strchr(++position,':')) && (end_char = strchr(++position,':'))){ //increment the position to skip the second column
*end_char = 0; //set the value 0 to end_char to terminate the string pointed by position
char * temp_str = "\0";
sprintf(temp_str, "{%d} - {%s}\n", atoi(position), line ); //concatenate line and userID into one string and store it into a temporary string
*(str + counter) = malloc(sizeof (char) * strlen(temp_str)); //set the memory space for the temp_string into the array
*(str + counter) = temp_str; //copy the string into the array
}
}
counter++;
}
printArray(str);
fclose(file);
if (line)
free(line);
return 0;
}
And here is the print function:
void printArray(char ** array){
for(int i = 0; i < 20; i++){
printf("%s",*(array+i));
free(*(array+i));
}
free(array);
}
I cannot find the error, the code compiles with
Process finished with exit code -1073741819 (0xC0000005)
So at least it compiles, and I think is just a problem in my pointers handling skills, but I'm not able to find the error.
Can someone help me?
there are 3 errors in your program :
use temp_str which haven't allocated.
char * temp_str = "\0";
sprintf(temp_str, "{%d} - {%s}\n", atoi(position), line );
save temp_str local pointer's address to str+counter and use the pointer after it went out of scope at printArray=> undefined behavior
line is not a pointer, can't use free
if (line)
{
free(line);
}
lets try this. https://godbolt.org/z/7KPfnTEMY I correct these points
I'm trying to read a file and a lines next line, to see if it starts with a space, for instance:
First line
second line
Third line
fourth line
Where when I'm reading the file in, I want to check and see if the following line, has a space, if it does, I want to strcat the two lines (in this case the first and second line).
So for the first example:
1.) Read in the first line, read ahead to the second, see that their is a space, and strcat both strings, making "First linesecond line" (Repeat for any other lines that follow this pattern).
Here's my go at it:
int main(void) {
FILE * file = fopen("file.txt","r");
if(file==NULL) { return 0; }
char * fileBuffer = malloc(sizeof(char)*100);
char * temp = malloc(sizeof(char)*100);
while(fgets(fileBuffer,100,file) != NULL) {
if(isspace(fileBuffer[0])) {
strcpy(temp,fileBuffer);
//store line that has a space in static temporary variable
}
else {
if(strcmp(temp,"") != 0) { //if temp is not empty
strcat(fileBuffer,temp);
}
}
}
free(fileBuffer);
free(temp);
return 0;
}
However this doesn't work. What happens is when fgets gets executed, it reads the first line, it sees there is no white space, and then goes to the next line, sees there is white space, stores it, but now fileBuffer doesn't contain the first line anymore, but the second.
So when I strcat the next time,I don't get the right result of "First linesecond line".
Instead i get the result of the third line mixed with the second, which is not what I want.
I'm not exactly sure how to fix my logic here, any ideas?
fix your logic like this:
#define LINE_SIZE 100
int main(void) {
FILE * file = fopen("file.txt","r");
if(file==NULL) { perror("fopen"); return -1; }
char * fileBuffer = malloc(LINE_SIZE);
char * temp = malloc(LINE_SIZE * 2);//Binding of the string is only once
while(fgets(fileBuffer, LINE_SIZE, file) != NULL) {
if(isspace(fileBuffer[0])) {
temp[strcspn(temp, "\n")] = 0;//remove newline
strcat(temp, &fileBuffer[1]);
printf("%s", temp);
}
else {
strcpy(temp, fileBuffer);
}
}
fclose(file);
free(fileBuffer);
free(temp);
return 0;
}
You have several additional considerations you will need to deal with. The first being, you need to remove the trailing '\n' include in the read by fgets. To do that, you will need the length of the line read. You can then remove the trialing '\n' by overwriting it with a nul-terminating character. e.g.:
while (fgets (buf1, MAXC, fp)) {
size_t len1 = strlen (buf1); /* get length */
if (len1 && buf1[len1-1] == '\n') buf1[--len1] = 0; /* remove \n */
Another consideration is how to manage allocation and freeing of the memory you use for the combined line. Since you are reading both the first part and then second part of your final line from relatively fixed length strings, it would make more sense to use 2 static buffers, one for reading the line, and one for holding a copy of the first. You can allocate for the final result. e.g.
enum { MAXC = 100 }; /* constant for max characters */
...
int main (int argc, char **argv) {
char buf1[MAXC] = {0};
char buf2[MAXC] = {0};
char *both = NULL;
Once you have both parts of your line ready to combine, you can allocate exactly the space needed, e.g.
if (*buf1 == ' ' && *buf2) {
both = malloc (len1 + strlen (buf2) + 1);
strcpy (both, buf2);
strcat (both, buf1);
...
Don't forget to free both after each allocation. Putting the pieces together, and fixing the logic of your comparisons, you could end up with a solution like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
enum { MAXC = 100 };
int main (int argc, char **argv) {
char buf1[MAXC] = {0};
char buf2[MAXC] = {0};
char *both = NULL;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n,", argv[1]);
return 1;
}
while (fgets (buf1, MAXC, fp)) {
size_t len1 = strlen (buf1);
if (len1 && buf1[len1-1] == '\n') buf1[--len1] = 0;
if (*buf1 == ' ' && *buf2) {
both = malloc (len1 + strlen (buf2) + 1);
strcpy (both, buf2);
strcat (both, buf1);
printf ("'%s'\n", both);
free (both);
*buf2 = 0;
}
else
strcpy (buf2, buf1);
}
if (fp != stdin) fclose (fp);
return 0;
}
Output
$ ./bin/cpcat ../dat/catfile.txt
'First line second line'
'Third line fourth line'
Look it over and let me know if you have any questions.
Really a quick and dirty way
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
int main(void) {
FILE * file = fopen("file.txt", "r");
if (file == NULL) { return 0; }
char fileBuffer[100];
char temp[100];
int first = 1;
while (fgets(fileBuffer, 100, file) != NULL) {
if (isspace(fileBuffer[0])) {
strcat(temp, fileBuffer);
//store line that has a space in static temporary variable
}
else {
if (first == 1){
strncpy(temp, fileBuffer, sizeof(temp));
// Remove the end line
temp[strlen(temp) - 1] = 0;
strcat(temp, " ");
first = 0;
}
else{
if (strcmp(temp, "") != 0) { //if temp is not empty
strcat(temp, fileBuffer);
// Remove the end line
temp[strlen(temp) - 1] = 0;
strcat(temp, " ");
}
}
}
}
printf("%s", temp);
free(fileBuffer);
free(temp);
return 0;
}
Output:
I'm working on a little C program for a class that reads the lines in from a file and then sorts them using qsort. Long story short, I am dynamically allocating memory for every line of a file, stored as a char*, in an array of char*. The reading in and storing ostensibly works fine based upon the output (see below), but when I print out the lines, they are all duplicates of the last line in the file. Can anyone point out my (most likely painfully obvious) error?
Here is the relevant code to the problem I'm currently running into:
char* trim_white_space(char* str);
char* get_line(FILE* infile, char temp[]);
int main(int argc, char* argv[]) {
FILE* infile;
char* input_file = argv[1];
int cnt = 0;
char temp[MAX_LINE_LENGTH]; //to hold each line as it gets read in
char* tempPointer = temp;
if (argc < 2) {
printf("No input file provided");
return EXIT_FAILURE;
}
//determine the number of lines in the file
infile = fopen(input_file, "r");
int num_lines_in_file = num_lines(infile);
fclose(infile);
//allocate pointers for each line
char** lines = (char**) malloc(num_lines_in_file * sizeof(char*));
//temporarily store each line, and then dynamically allocate exact memory for them
infile = fopen(input_file, "r");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
lines[cnt] = trim_white_space(tempPointer);
printf("%d: %s\n", cnt, lines[cnt]);
}
fclose(infile);
//print the unsorted lines (for debugging purposes)
printf("Unsorted list:\n");
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
printf("%s\n", lines[cnt]);
}
char* get_line(FILE* infile, char temp[]) {
fgets(temp, MAX_LINE_LENGTH-1, infile);
char* pntr = temp;
return pntr;
}
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace(*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace(*end)) end--;
// Write new null terminator
*(end+1) = 0;
return str;
}
I have this sample input file 5-1input.dat:
Hi guys
x2 My name is
Slim Shady
For real
And here's the output I'm getting:
user#user-VirtualBox ~/Desktop/Low-level/HW5 $ ./homework5-1 5-1input.dat
0: Hi guys
1: x2 My name is
2: Slim Shady
3: For real
Unsorted list:
For real
For real
For real
For real
As in the comments, you should change your loop to:
for (cnt = 0; cnt != num_lines_in_file; cnt++) {
tempPointer = get_line(infile, temp);
lines[cnt] = (char*) malloc(strlen(tempPointer) + 1);
strncpy(lines[cnt], trim_white_space(tempPointer), strlen(tempPointer)+1);
printf("%d: %s\n", cnt, lines[cnt]);
}
The size in strncpy is based on the size of malloc you've used.
Of course you can optimize this code, e.g. to count strlen only once, etc.
I am doing an exercise on a book, changing the words in a sentence into pig latin. The code works fine in window 7, but when I compiled it in mac, the error comes out.
After some testings, the error comes from there. I don't understand the reason of this problem. I am using dynamic memories for all the pointers and I have also added the checking of null pointer.
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
Full source code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define inputSize 81
void getSentence(char sentence [], int size);
int countWord(char sentence[]);
char ***parseSentence(char sentence[], int *count);
char *translate(char *world);
char *translateSentence(char ***words, int count);
int main(void){
/* Local definition*/
char sentence[inputSize];
int wordsCnt;
char ***head;
char *result;
getSentence(sentence, inputSize);
head = parseSentence(sentence, &wordsCnt);
result = translateSentence(head, wordsCnt);
printf("\nFinish the translation: \n");
printf("%s", result);
return 0;
}
void getSentence(char sentence [81], int size){
char *input = (char *)malloc(size);
int length;
printf("Input the sentence to big latin : ");
fflush(stdout);
fgets(input, size, stdin);
// do not copy the return character at inedx of length - 1
// add back delimater
length = strlen(input);
strncpy(sentence, input, length-1);
sentence[length-1]='\0';
free(input);
}
int countWord(char sentence[]){
int count=0;
/*Copy string for counting */
int length = strlen(sentence);
char *temp = (char *)malloc(length+1);
strcpy(temp, sentence);
/* Counting */
char *pToken = strtok(temp, " ");
char *last = NULL;
assert(pToken == temp);
while (pToken){
count++;
pToken = strtok(NULL, " ");
}
free(temp);
return count;
}
char ***parseSentence(char sentence[], int *count){
// parse the sentence into string tokens
// save string tokens as a array
// and assign the first one element to the head
char *pToken;
char ***words;
char *pW;
int noWords = countWord(sentence);
*count = noWords;
/* Initiaze array */
int i;
words = (char ***)calloc(noWords+1, sizeof(char **));
for (i = 0; i< noWords; i++){
words[i] = (char **)malloc(sizeof(char *));
}
/* Parse string */
// first element
pToken = strtok(sentence, " ");
if (pToken){
pW = (char *)malloc(strlen(pToken)+1);
strcpy(pW, pToken);
**words = pW;
/***words = pToken;*/
// other elements
for (i=1; i<noWords; i++){
pToken = strtok(NULL, " ");
pW = (char *)malloc(strlen(pToken)+1);
strcpy(pW, pToken);
**(words + i) = pW;
/***(words + i) = pToken;*/
}
}
/* Loop control */
words[noWords] = NULL;
return words;
}
/* Translate a world into big latin */
char *translate(char *word){
int length = strlen(word);
char *bigLatin = (char *)malloc(length+3);
/* translate the word into pig latin */
static char *vowel = "AEIOUaeiou";
char *matchLetter;
matchLetter = strchr(vowel, *word);
// consonant
if (matchLetter == NULL){
// copy the letter except the head
// length = lenght of string without delimiter
// cat the head and add ay
// this will copy the delimater,
strncpy(bigLatin, word+1, length);
strncat(bigLatin, word, 1);
strcat(bigLatin, "ay");
}
// vowel
else {
// just append "ay"
strcpy(bigLatin, word);
strcat(bigLatin, "ay");
}
return bigLatin;
}
char *translateSentence(char ***words, int count){
char *bigLatinSentence;
int length = 0;
char *bigLatinWord;
/* calculate the sum of the length of the words */
char ***walker = words;
while (*walker){
length += strlen(**walker);
walker++;
}
/* allocate space for return string */
// one space between 2 words
// numbers of space required =
// length of words
// + (no. of words * of a spaces (1) -1 )
// + delimater
// + (no. of words * ay (2) )
int lengthOfResult = length + count + (count * 2);
bigLatinSentence = (char *)malloc(lengthOfResult);
// trick to initialize the first memory
strcpy(bigLatinSentence, "");
/* Translate each word */
int i;
char *w;
for (i=0; i<count; i++){
w = translate(**(words + i));
strcat(bigLatinSentence, w);
strcat(bigLatinSentence, " ");
assert(w != **(words + i));
free(w);
}
/* free memory of big latin words */
walker = words;
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
return bigLatinSentence;
}
Your code is unnecessarily complicated, because you have set things up such that:
n: the number of words
words: points to allocated memory that can hold n+1 char ** values in sequence
words[i] (0 <= i && i < n): points to allocated memory that can hold one char * in sequence
words[n]: NULL
words[i][0]: points to allocated memory for a word (as before, 0 <= i < n)
Since each words[i] points to stuff-in-sequence, there is a words[i][j] for some valid integer j ... but the allowed value for j is always 0, as there is only one char * malloc()ed there. So you could eliminate this level of indirection entirely, and just have char **words.
That's not the problem, though. The freeing loop starts with walker identical to words, so it first attempts to free words[0][0] (which is fine and works), then attempts to free words[0] (which is fine and works), then attempts to free words (which is fine and works but means you can no longer access any other words[i] for any value of i—i.e., a "storage leak"). Then it increments walker, making it more or less equivalent to &words[1]; but words has already been free()d.
Instead of using walker here, I'd use a loop with some integer i:
for (i = 0; words[i] != NULL; i++) {
free(words[i][0]);
free(words[i]);
}
free(words);
I'd also recommending removing all the casts on malloc() and calloc() return values. If you get compiler warnings after doing this, they usually mean one of two things:
you've forgotten to #include <stdlib.h>, or
you're invoking a C++ compiler on your C code.
The latter sometimes works but is a recipe for misery: good C code is bad C++ code and good C++ code is not C code. :-)
Edit: PS: I missed the off-by-one lengthOfResult that #David RF caught.
int lengthOfResult = length + count + (count * 2);
must be
int lengthOfResult = length + count + (count * 2) + 1; /* + 1 for final '\0' */
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
/* free(walker); Don't do this, you still need walker */
walker++;
}
free(words); /* Now */
And you have a leak:
int main(void)
{
...
free(result); /* You have to free the return of translateSentence() */
return 0;
}
In this code:
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
free(walker);
walker++;
}
You need to check that **walker is not NULL before freeing it.
Also - when you compute the length of memory you need to return the string, you are one byte short because you copy each word PLUS A SPACE (including a space after the last word) PLUS THE TERMINATING \0. In other words, when you copy your result into the bigLatinSentence, you will overwrite some memory that isn't yours. Sometimes you get away with that, and sometimes you don't...
Wow, so I was intrigued by this, and it took me a while to figure out.
Now that I figured it out, I feel dumb.
What I noticed from running under gdb is that the thing failed on the second run through the loop on the line
free(walker);
Now why would that be so. This is where I feel dumb for not seeing it right away. When you run that line, the first time, the whole array of char*** pointers at words (aka walker on the first run through) on the second run through, when your run that line, you're trying to free already freed memory.
So it should be:
while (walker != NULL && *walker != NULL){
free(**walker);
free(*walker);
walker++;
}
free(words);
Edit:
I also want to note that you don't have to cast from void * in C.
So when you call malloc, you don't need the (char *) in there.
I am new to C but I am trying to write a function that returns a line from a file depending on the parameter used. It will return the last line that contains that parameter. I think it's better explained using an example:
Here is the contents of the file:
1 one onehello
2 two twohello
3 one threehello
So, if I call the function like this:
lineContaining("one")
It should return "one threehello"
Here is what I have so far, it also includes a main function that tests the function:
char *readStringedCommand(char *str1)
{
int size = 1024;
char *buffer = malloc(size);
char *result = malloc(size);
FILE *fp;
fp = fopen("test.txt", "r");
while(fgets(buffer, 1024, fp)) //get a line from a file
{
printf("while1 entered: %s", buffer);
int i = 0;
int j = 0;
int k = 0;
while(buffer[i] != '\n') //read all the way to the end of a line
{
printf("while2 entered: %s", buffer+i);
k = i;
while(buffer[k]==str1[j]) //while two characters match
{
printf("while3 entered");
k++;
j++;
strcat(result, buffer+k); //append the character to the result
if(str1[j] = '\0') //if the next character of str1 is the last one
{
strncat(result, buffer+k, 20); //append the rest of buffer to the result
return result;
printf("result = %s", result);
}
}
result[0] = '\0'; //clear result for the next line
j = 0; //set str1's position to 0
k = 0;
i++;
}
}
return "errorrrrrr";
}
int main(int argc, const char* argv[])
{
int num1 = 1;
char str1[] = "one onehello";
int num2 = 2;
char str2[] = "two twohello";
int num3 = 3;
char str3[] = "one threehello";
hwrite(num1, str1); //a function I made that writes a line to a file
hwrite(num2, str2);
hwrite(num3, str3);
printf("%s", readStringedCommand("one"));
return 0;
}
Okay, the function gives me an error:
while1 entered: 1 one onehello
while2 entered: 1 one onehello
while2 entered: one onehello
while2 entered: one onehello
Segmentation fault (core dumped)
Considering it gives me the error at the third while loop, I think the problem is there. I unfortunately don't know what is wrong here. I am sure there are more errors after that point but this one is confusing me.
MY QUESTIONS:
How do I fix this segmentation error?
The code is obviously very ugly but I suck at C. Is there a better way to solve this question?
Thanks for reading all of this and I would appreciate some help. =(
EDIT: After fixing some errors suggested by you guys, I no longer get the segmentation error. The function returns " onehello" instead, which is wrong. It should return "one threehello". But I am making progress, and for that I am thankful.
if(str1[j] = '\0')
should be
if(str1[j] == '\0')
you probably want to compare values
the loop while(buffer[i] != '\n') might not exit if your file is missing a newline character, what might happen in the last line of in .txt file.
So if I understand your problem correctly, your goal can be achieved in a much simpler way:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char *readStringedCommand(char *str1)
{
int size = 1024;
char *buffer = malloc(size);
char *result = malloc(size);
/* Check allocations */
if (!buffer || !result) {
fprintf(stderr, "Allocation failure, exiting.\n");
exit(EXIT_FAILURE);
}
result[0] = 0; // 0-terminate result
FILE *fp;
fp = fopen("test.txt", "r");
/* Check whether file was opened successfully */
if (!fp) {
fprintf(stderr, "Couldn't open file.\n");
exit(EXIT_FAILURE);
}
while(fgets(buffer, size, fp)) //get a line from a file
{
if (strstr(buffer, str1)) { // Check whether the line contains the needle
strcpy(result, buffer); // Copy the line if it does
}
}
// close file
fclose(fp);
// Free memory we don't need anymore
free(buffer);
// Now, what if no line contained the needle?
if (result[0] == 0) {
// Hmm, maybe
free(result);
return NULL; // ?
} else {
return result;
}
}
Just use strstr from the standard library to check whether str1 is contained in each line, copy each line that contains it to result, ignore the others. Then at the end, result contains the last line that contained str1 - if any.
If you don't want the entire line, but only the part starting at the first occurrence of str1, capture the pointer that strstr returns, and use that as the source for the strcpy:
char *needle;
if ((needle = strstr(buffer, str1))) {
strcpy(result, needle);
}