I am given a text file and I need to put it in a buffer and use get_lines to make an array of pointers after converting each line to a string. I am having trouble with just the get_lines function as I am getting a seg fault when run it.
Here's my code:
#include <stdlib.h>
#include <stdio.h>
int readfile(FILE *fp, char **cbuf);
char **get_lines(char *cbuf, int bufsize, int word);
int readword(FILE*tmp);
int main(int argc, char *argv[])
{
int i,bufsize, num_word;
char *cbuf;
char **lines;
FILE *fp;
FILE *tmp;
if( (fp=fopen(argv[1],"r")) == NULL)
{
perror("ERROR: bad/no filename");
exit(0);
}
tmp = fopen(argv[1],"r");
bufsize = readfile(fp,&cbuf);
num_word = readword(tmp);
lines = get_lines(cbuf, bufsize, num_word) ;
i=0;
while( lines[i] != NULL) {
printf("%i\t%s\n",i,lines[i]);
i++;
}
return 0;
}
int readfile(FILE *fp,char**cbuf)
{
int i;
char c;
fseek(fp, 0L, SEEK_END);
int bufsize = ftell(fp);
fseek(fp, 0L, SEEK_SET);
*cbuf = (char *)malloc(sizeof(char) * bufsize);
for (i = 0; i < bufsize; i++)
{
c = fgetc(fp);
(*cbuf)[i] = c;
}
return bufsize;
}
int readword(FILE*tmp)
{
int word = 0;
char c;
while((c = fgetc(tmp)) != EOF )
{
if (c == '\n')
word++;
}
return word;
}
char **get_lines(char *cbuf, int bufsize, int word)
{
int i = 0, j = 0, counter = 0;
char (*lines)[bufsize];
lines = (char**)malloc(sizeof(char*)*bufsize);
counter = cbuf;
for (i = 0; i < bufsize; i++)
{
if(cbuf[i] == '\n')
{
cbuf[i] == '\0';
counter = cbuf[i + 1];
j++;
}else
{
*lines[j] = &counter;
}
}
lines[word] == NULL;
return lines;
}
The violation causing the fault is not immediately obvious to me, can someone tell me what might be wrong in get_lines()?
This code is wrong:
char (*lines)[bufsize];
lines = (char**)malloc(sizeof(char*)*bufsize);
It allocates a pointer to an array of char. Then you malloc the wrong amount of space, cast to the wrong type, and write *lines[j] = &counter; which tries to store a pointer in a char.
You should get many compiler errors/warnings for the get_lines function. It's important to pay attention to such messages as they are telling you that something is wrong with your code. There's no point even starting to investigate a segfault until you have fixed all the errors and warnings.
See here for a great guide on how to debug your code; I suspect you would fail the rubber duckie test on the get_lines function.
Here is a fixed version (untested):
// Precondition: cbuf[bufsize] == '\0'
//
char **get_lines(char *cbuf, size_t bufsize, size_t num_lines)
{
// +1 for the NULL termination of the list
char **lines = malloc((num_lines + 1) * sizeof *lines);
size_t line = 0;
while ( line < num_lines )
{
lines[line++] = cbuf;
cbuf = strchr(cbuf, '\n');
if ( !cbuf )
break;
*cbuf++ = '\0';
}
lines[line] = NULL;
return lines;
}
In your existing code there is no room to write the null terminator for the last line; my advice is to make readfile actually malloc one extra byte and make sure that is set to 0.
Related
I am working on a program that reads text, line by line from input file. Once the line is read, the program reverses order of words in that string, prints it to the output file and starts reading next line. My program reads only specific number of characters from one line, meaning that if line contains more characters then that specific number, all of them have to skipped until next line is reached. My program seems to work fine.
One of the task requirements is to use dynamically allocated arrays. That is the part where my main problem lies. Once I try to free heap-allocated memory, the program fails with error message that says: HEAP CORRUPTION DETECTED. It must be that I messed up something while working with them. However, I am unable to find the real reason.
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SIZE 255
int readLine(FILE** stream, char** buffer, int* bufferSize);
void reverseString(char* buffer, char** reverse, int bufferSize, int lastLine);
int main(int argc, char** argv)
{
char* buffer = NULL;
char* reverse = NULL;
int bufferSize = 0;
int lastLine = 0;
FILE* intputStream = fopen(argv[1], "r");
FILE* outputStream = fopen(argv[2], "w");
if (intputStream == NULL || outputStream == NULL)
{
printf("Input or output file cannot be opened\n");
return 0;
}
while (!feof(intputStream))
{
lastLine = readLine(&intputStream, &buffer, &bufferSize);
reverse = (char*)malloc(sizeof(char) * bufferSize);
if (reverse != NULL)
{
reverseString(buffer, &reverse, bufferSize, lastLine);
fputs(reverse, outputStream);
}
}
fclose(intputStream);
fclose(outputStream);
free(buffer);
free(reverse);
return 0;
}
int readLine(FILE** stream, char** buffer, int* bufferSize)
{
char tempBuffer[BUFFER_SIZE] = { 0 };
int lastLine = 0;
if (*stream != NULL)
{
fgets(tempBuffer, BUFFER_SIZE, *stream);
char ignoredChar[100] = { 0 };
*bufferSize = strlen(tempBuffer);
// Ignoring in the same line left characters and checking if this is the last line
if (tempBuffer[(*bufferSize) - 1] != '\n')
{
fgets(ignoredChar, 100, *stream);
if (!feof(*stream))
lastLine = 1;
}
// Allocating memory and copying line to dynamically-allocated array
*buffer = (char*)malloc(sizeof(char) * (*bufferSize));
if (*buffer != NULL)
{
memcpy(*buffer, tempBuffer, (*bufferSize));
(*buffer)[(*bufferSize)] = '\0';
}
}
// Return whether or not the last line is read
return lastLine;
}
void reverseString(char* buffer, char** reverse, int bufferSize, int lastLine)
{
int startingValue = (lastLine ? bufferSize - 1 : bufferSize - 2);
int wordStart = startingValue, wordEnd = startingValue;
int index = 0;
while (wordStart > 0)
{
if (buffer[wordStart] == ' ')
{
int i = wordStart + 1;
while (i <= wordEnd)
(*reverse)[index++] = buffer[i++];
(*reverse)[index++] = ' ';
wordEnd = wordStart - 1;
}
wordStart--;
}
for (int i = 0; i <= wordEnd; i++)
{
(*reverse)[index] = buffer[i];
index++;
}
if (!lastLine)
(*reverse)[index++] = '\n';
(*reverse)[index] = '\0';
}
One of the problems is in readLine where you allocate and copy your string like this (code shortened to the relevant parts):
*bufferSize = strlen(tempBuffer);
*buffer = (char*)malloc(sizeof(char) * (*bufferSize));
(*buffer)[(*bufferSize)] = '\0';
This will not allocate space for the null-terminator. And you will write the null-terminator out of bounds of the allocated memory. That leads to undefined behavior.
You need to allocate an extra byte for the null-terminator:
*buffer = malloc(*bufferSize + 1); // +1 for null-terminator
[Note that I don't cast the result, and don't use sizeof(char) because it's specified to always be equal to 1.]
Another problem is because you don't include the null-terminator in the bufferSize the allocation for reverse in main will be wrong as well:
reverse = (char*)malloc(sizeof(char) * bufferSize);
Which should of course be changed to:
reverse = malloc(bufferSize + 1); // +1 for null-terminator
hello guys a just need help on this, not showing the text I wrote:
This program open the file and just show on command what is inside,
if buffer is > 0 show all the text contained in file.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
char *ft_strncat(char *dst, const char *src, size_t n)
{
if (n != 0) {
char *d = dst;
const char *s = src;
while (*d != 0)
d++;
do {
if ((*d = *s++) == 0)
break;
d++;
} while (--n != 0);
*d = 0;
}
return (dst);
}
char *get_next_line(int fd)
{
char buffer[2] = "";
char **line;
if( !*line )
*line = malloc(100 * sizeof(char));
*line[0] = '\0';
while( read(fd, buffer, 1) > 0 ) {
ft_strncat(*line, buffer, 1);
if( buffer[0] == '\n' )
break;
}
return (0);
}
int main(void)
{
int fd;
int ret;
fd = open("ola.txt", O_RDONLY);
if (fd < 3 && fd != 0)
return (-1);
printf("%d\n", fd);
printf("%s\n", get_next_line(fd));
return (0);
}
im trying to see the error but I cant, im a noob on C yet
thank you for help me.
line should be char *, not char **. That would only be needed if it were a function parameter that should be updated by the function.
You need to return line from the function, not 0.
You should use realloc() to grow line if the input line is longer than the size of line. Use a variable capacity to hold the current size.
There's no good reason to use ft_strncat(). Use another variable to hold the current position in line, and write the character there directly.
char *get_next_line(int fd)
{
char buffer;
size_t capacity = 100;
char *line = malloc(capacity * sizeof(char));
size_t pos = 0;
*line[0] = '\0';
while( read(fd, &buffer, 1) > 0 ) {
if (pos > capacity - 2) {
capacity += 100;
line = realloc(line, capacity);
}
line[pos++] = buffer;
if( buffer == '\n' ) {
line[pos] = '\0';
break;
}
}
return line;
}
In addition, the caller should assign the result to a variable, so it can free the memory. Otherwise you'll create lots of memory leaks when you read all the lines of the file.
i have a file (for example lorem.txt)
so now i have two task ,
was to read file and remove space and lines.
to determine the two letter combination that occurs most often in my file.
i have done the first part. can anyone tell me what can i do to 2. part? I tried various solutions, but was unable to do it. Let me know, if I can provide anything else.
#include <stdio.h>
#include <stdlib.h> // For exit()
FILE* pInputFile;
int chr = 0;
int main()
{
FILE* fptr;
char c;
char filename[] = "Lorem.txt";
char *str, *strblank;
// char str[469], strblank[469];
int i = 0;
//int x=500;
errno_t err;
if ((err = fopen_s(&pInputFile, "Lorem.txt", "r")) == 0)
{
size_t pos = ftell(pInputFile); // Current position
fseek(pInputFile, 0, SEEK_END); // Go to end
size_t length = ftell(pInputFile); // read the position which is the size
fseek(pInputFile, pos, SEEK_SET); // restore original position
//x = length;
//char str[x], strblank[x];
printf("%d", length);
str = malloc(length * sizeof(char));
strblank = malloc(length * sizeof(char));
// file exists: don't read a char before the loop or
// it will be lost
while ((chr = getc(pInputFile)) != EOF)
{
//printf("%c", chr); //This line prints the file lines.
str[i] = chr;
i++;
}
i= 0;
printf("%s", str);
removespace(str, strblank);
printf("%s", strblank);
fclose(pInputFile);
}
else
{
fprintf(stderr, "Cannot open file, error %d\n", err);
// handle the error further if needed
}
return 0;
}
int removespace(char aj[500],char mj[500])
{
//char mj[500];
int i = 0, j = 0, len;
//printf("\n\nEnter the string: ");
//gets(aj);
len = strlen(aj); // len stores the length of the input string
while (aj[i] != '\0') // till string doesn't terminate
{
if (aj[i] != ' ' && aj[i] != '\n') // if the char is not a white space
{
/*
incrementing index j only when
the char is not space
*/
mj[j++] = aj[i];
}
/*
i is the index of the actual string and
is incremented irrespective of the spaces
*/
i++;
}
mj[j] = '\0';
// printf("\n\nThe string after removing all the spaces is: ");
return 0;
}
So I have been searching through stack overflow for a little over an hour and I don't understand why this function is giving me a segmentation error. I want to create a string array, scan strings in through scanf, dynamically change the size of each string and return the string array. Can anyone help? Thank you.
char** readScores(int* count) {
int c = 0;
char** arr =(char**)malloc(100 * sizeof(char*));
char* in;
while(scanf("%s", in) != EOF) {
arr[c] = (char*)malloc(strlen(in)+1);
strcpy(arr[c], in);
}
*count = c;
return arr;
}
char* in;
while(scanf("%s", in) != EOF) {
This tells the computer to read from standard input into the char buffer that in points to.
Which does not exist, because in is not initialised to anything (let alone a valid buffer).
I would not use scanf only fgets.
You need to allocate memory dor the arr and for every line referenced by elements of arr
char** readScores(size_t *count) {
size_t lines = 0;
char** arr = NULL, **tmp;
char* in = malloc(MAXLINE), *result;
size_t len;
if(in)
{
do{
result = fgets(in, MAXLINE, stdin);
if(result)
{
len = strlen(in);
tmp = realloc(arr, sizeof(*tmp) * (lines + 1));
if(tmp)
{
arr = tmp;
len = strlen(in);
arr[lines] = malloc(len + (len == 0));
if(arr[lines])
{
if(len) memcpy(arr[lines], in, len - 1);
arr[lines++][len] = 0;
}
else
{
// error handling
}
}
else
{
// error handling
}
}
}while(result);
free(in);
}
*count = lines;
return arr;
}
I'm trying to parse through some CSV log files extracting only the n'th field (dismissing the others for speed). My function works as expected when I use a buffer size with fread greater than the size of the input.
The problem is when I read in part of the input and try to continue where I left off the next time the function is called. I believe the problem lies in how I'm handling the null terminator and setting my globals, but I just can't seem to figure it out.
Any help with understanding what I'm doing wrong greatly appreciated!
Code
#include <stdio.h>
#include <time.h>
int gcomc = 0;
int gpos = 0;
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
char so[10];
int comc = gcomc;
int pos = gpos;
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
comc++;
break;
case '\0':
gcomc = comc;
gpos = pos;
break;
default:
if (comc == 3) {
ch = ptr[i];
so[pos++] = ch;
}
if (comc == 7) {
printf(" %s ", so);
comc = 0;
pos = 0;
gcomc = 0;
gpos = 0;
}
}
}
return;
}
int main(int argc, char* argv[])
{
FILE *fin=fopen("test.txt", "rb");
char buffer[100 + 1];
size_t bsz;
while((bsz = fread(buffer, sizeof *buffer, 100, fin)) > 0)
{
buffer[bsz] = '\0';
test(buffer, bsz);
}
return 1;
}
Input
A;B;C;D;E;F;G;H
I;J;K;L;M;N;O;P
Q;R;S;T;U;V;W;X
Y;Z;1;2;3;4;5;6
Output with buffer size of 100 (101)
D L T 2
Output with buffer size of 10 (11)
D P
Q X
Segmentation fault (core dumped)
Edit:
Thank you for the comments and code, I've reworked my (rather dumb written) code - any further criticism is welcome (constructive or destructive, I learn from it all):
#include <stdio.h>
#include <time.h>
void test(char *str, int len);
int gcomc, gpos = 0;
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
static char so[10];
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
gcomc++;
break;
default:
if (gcomc == 3) {
ch = ptr[i];
so[gpos++] = ch;
}
if (gcomc == 7) {
so[gpos] = '\0'; /* ensure so is null terminated */
printf(" %s ", so);
gcomc = 0;
gpos = 0;
}
}
}
return;
}
extern int main()
{
FILE *fin=fopen("test.txt", "rb");
char buffer[10 + 1];
size_t bsz;
while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0)
{
test(buffer, bsz);
}
return 1;
}
There are at least two problems in your code to be able to read the file in chunks.
First, the so array is automatic: it has no reason to keep its values from one call to the others. You should declare it global (outside the test function) or static.
Next, you only copy the local state to global one when you find a null. But the null is at position len, and you exit the loop just before (for(i = 0; i < len; i++) note the <) so on next call you start again with 0, 0. You should choose one method to indicate the end of the buffer, either passing a length, of writing a null marker, but mixing both is error prone. As you use fread, my advice is to stick to a length:
In main use:
while((bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin)) > 0)
{
test(buffer, bsz);
}
(that way, you only write the size of the buffer once)
and in test:
void test(char *str, int len)
{
const char *ptr = str;
char ch;
int i;
static char so[10];
int comc = gcomc;
int pos = gpos;
for(i = 0; i < len; i++)
{
ch = ptr[i];
switch(ch)
{
case ';':
comc++;
break;
default:
if (comc == 3) {
ch = ptr[i];
so[pos++] = ch;
}
if (comc == 7) {
so[pos] = '\0'; /* ensure so is null terminated */
printf(" %s ", so);
comc = 0;
pos = 0;
gcomc = 0;
gpos = 0;
}
}
}
gcomc = comc; /* store the state to globals */
gpos = pos;
return;
}
But as you were said in comments mixing local and globals like that is error prone. It looks like you started coding before designing the structure of the program and identifying what actually needed to be global. You didn't, did you? ;-)
The state of the parser inside test() need to survive the multiple call. You took care of this partly only make the counters global. Globals are bad practise. Also you miss to save the state (its content) of so.
Encapsulate the state in a structure.
#include <stdlib.h>
#include <stdio.h>
#define SO_SIZE (10)
struct state
{
size_t comc;
size_t pos;
char so[SO_SIZE + 1]; /* Add 1 for the 0-terminator. */
}
and pass it to each call of the parser (test() here).
Adjust the parser like this:
int test(struct state * pstate, const char *str, size_t len)
{
int result = 0; /* be optimistic. */
char ch;
size_t i;
for (i = 0; i <= len; i++)
{
ch = str[i];
switch (ch)
{
case ';':
pstate->comc++;
break;
default:
if (pstate->comc == 3)
{
ch = str[i];
if (SO_SIZE <= pstate->pos)
{
result = -1; /* overflow */
break;
}
pstate->so[pstate->pos++] = ch;
}
if (pstate->comc == 7)
{
printf(" %s ", pstate->so);
pstate->comc = 0;
pstate->pos = 0;
}
}
}
return result;
}
Then call it like this:
#define BUFFER_SIZE (100)
int main(void)
{
FILE *fin = fopen("test.txt", "rb");
if (NULL == fin)
{
perror("fopen() failed");
return EXIT_FAILURE;
}
{
char buffer[BUFFER_SIZE + 1] = {0};
size_t bsz;
struct state state = {0};
int result;
while (0 < (bsz = fread(buffer, sizeof *buffer, sizeof buffer, fin))
&& (0 == result))
{
result = test(&state, buffer, bsz);
}
return result ?EXIT_FAILURE :EXIT_SUCCESS;
}
}