Print last few lines of a text file - c

Currently, I am trying to create a C program that prints the last few lines of a text file, read in through the command line. However, it is currently causing a segmentation error when I try to copy the strings from fgets into the main array. I have been unable to fix this, and so have not been able to test the rest of my code. How would I begin to fix the segmentation error? I have posted the code below:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
int i=1,j,printNumber;
char **arr = (char **) malloc (100 * sizeof(char *));
char *line = (char *) malloc (80 * sizeof(char));
if (argc == 1) {
printNumber = 10;
}
else {
printNumber = atoi(argv[1]);
}
while (fgets(line,80,stdin) != NULL) {
if (line != NULL) {
line[strlen(line)-1] = '\0';
strcpy(arr[i],line); //SEGMENTATION ERROR!!!!
}
else {
free(line);
strcpy(arr[i],NULL);
}
i++;
printf("%d ",i);
}
free(arr);
for (j = i-printNumber-1; j < i-1; j++) {
printf("%s ", arr[j]);
}
printf("\n");
return 0;
}

You are allocating space for arr, which is a pointer to a pointer to char, but not allocating any individual char * pointers within arr.
Since you allocated arr with the size of 100 * sizeof(char *), I assume you want 100 sub-entries in arr. Sure:
for(i = 0; i < 100; i++)
arr[i] = malloc(80 * sizeof(char));
Then, when you free arr:
for(i = 0; i < 100; i++)
free(arr[i]);
free(arr);
Note that it is good practice to always check malloc for failure (return value of NULL) and handle it, and to set pointers to NULL after freeing them once to avoid double-free bugs.

You don't always know the length of the longest line (not until you try to read) OR how many last lines you are expected to keep track of (but is given at runtime). Thus, both of these values need to be known before you allocate memory or delegated to a function that does it for you.
#include <stdio.h>
#include <stdlib.h>
struct Line {
char *line; // content
size_t storage_sz; // allocation size of line memory
ssize_t sz; // size of line, not including terminating null byte ('\0')
};
int main(int argc, char *argv[]) {
int max_lines = 10;
if (argc > 1) {
max_lines = atoi(argv[1]);
}
if (max_lines < 0) {
fprintf(stderr, "%s\n", "Sorry, no defined behaviour of negative values (yet)\n");
return EXIT_FAILURE;
}
// keep an extra slot for the last failed read at EOF
struct Line *lines = (struct Line *) calloc(max_lines + 1, sizeof(struct Line));
int end = 0;
int size = 0;
// only keep track of the last couple of lines
while ((lines[end].sz = getline(&lines[end].line, &lines[end].storage_sz, stdin)) != -1) {
end++;
if (end > max_lines) {
end = 0;
}
if (size < max_lines) {
size++;
}
}
// time to print them back
int first = end - size;
if (first < 0) {
first += size + 1;
}
for (int count = size; count; count--) {
// lines might contain null bytes we can't use printf("%s", lines[first].line);
fwrite(lines[first].line, lines[first].sz, 1u, stdout);
first++;
if (first > size) {
first = 0;
}
}
// clear up memory after use
for (int idx = 0; idx <= max_lines; idx++) {
free(lines[idx].line);
}
free(lines);
return EXIT_SUCCESS;
}

Related

How to input data from a text file into a pointer array?

I have a text file I want to upload into a pointer array but I can't find any references besides 2D arrays or languages other than C.
My input.txt:
marbels
fruit
vegetables
marshmellow sprinkle
coffe beans
My source.c (my question is located in the int main(void){})
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define MAX_LEN 1000
void sort(size_t size, char* data[]) {
for (int i = 0; i < size - 1; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (strlen(data[j]) < strlen(data[j + 1])) {
char* temp = data[j];
data[j] = data[j + 1];
data[j + 1] = temp;
}
}
}
}
void printArray(size_t size, char* data[]) {
for (int i = 0; i < size; i++) {
printf("%s ", data[i]); //%c -> %s
}
}
int main(void) {
char* data[1000]; //I want the array to hold maximum 1000 characters
FILE* file;
file = fopen("C:\\Users\\EXAMPLE\\desktop\\input.txt", "r");
if (file == NULL) {
printf("File Error\n", file);
return 1;
}
//between these dashes in my issue with uploading the text from input to the char* data[1000];
int line = 0;
//if i build the program with this while-loop, I get an error
while (!eof(file) && ferror(file)) {
if (fgets(data[line], MAX_LEN, file) != NULL) { //error C6001: using uninitialized memory 'data'
line++;
}
}
fclose(file);
//
size_t size = sizeof data / sizeof data[0];
sort(size, data);
printArray(size, data);
return 0;
}
The error message:
I tried that while loop before and it wasn't great but only thing I could find at the time for this project.
As you have mentioned in your question, you will need to use a 2D array or otherwise you can use the malloc() function.
In your code you have a array with 1000 * sizeof(char) bytes. So bassically you have 1000 uninitialised pointers. But there are no pointers allocated that are able to store the lines of the file.

C - Sorting a dynamically sized array of strings

I'm quite new to C and trying to read an input stream of data from a file, appending it to a dynamically sized array, which works well to that point.
After that, I want to sort the array alphabetically. I read the manpages of strcmp and think it's the best way to do this. However, I get hit by a "Segmentation Fault" whenever i'm trying to execute it. What exactly am I doing wrong when allocating the memory?
Here is my code for reference:
int main (int argc, char* argv[]) {
int c = getLineCount();
FILE * wordlist = fopen("test", "r");
// If file doesn't exist, handle error
if ( wordlist == NULL ) {
fputs("Unable to open input file", stderr);
exit(1);
}
int length = 101;
char line[length];
int i = 0;
char **words = malloc(c * sizeof(line));
if ( words == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
while (1) {
char *l = fgets(line, length, wordlist);
if ( (l == NULL) ) {
// Check if EOF is reached
if (feof(wordlist)) {
// fputs("--- EOF ---\n", stdout);
break;
// Check if error occured while reading
} else {
fputs("Error reading file", stderr);
exit(1);
}
} else if (strchr(line, '\n') == NULL) {
// Check if line is too long
// Iterate until newline is found or until EOF
int c;
while((c = fgetc(wordlist)) != '\n' && c != 0);
fputs("--- LINE TOO LONG ---\n", stderr);
continue;
} else if ( line[0] == '\n' ) {
// Check if line is only "\n", if yes, ignore the line
continue;
} else {
words[i] = malloc(sizeof(line) * sizeof(char));
if ( words[i] == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
strcpy(words[i], line);
i++;
}
}
// Close file
fclose(wordlist);
char temp[101];
for (int i = 0; i < (length-1); i++) {
int lowest = i;
for (int j = i+1; j < length; j++) {
if (strcmp(words[j], words[lowest]) < 0) {
lowest = j;
}
}
if (lowest != i) {
strcpy(temp, words[i]);
words[i] = words[lowest];
free(words[lowest]);
words[lowest] = malloc(sizeof(temp) * sizeof(char));
if ( words[lowest] == NULL ) {
fputs("Unable to allocate Memory", stderr);
exit(1);
}
strcpy(words[lowest], temp);
}
}
// Print out array
fputs("--- ARRAY ---\n\n", stdout);
for (int i = 0; i < c; i++) {
fputs(words[i], stdout);
}
exit(0);
}
The upper bounds of the sorting algorithm is length, which is incorrect, as length is the size of the input line buffer.
for (int i = 0; i < (length-1); i++) {
The upper bounds should be i from the outer scope here, as it is incremented when a new line is added to the array:
strcpy(words[i], line);
i++;
This upper bounds
for (int i = 0; i < c; i++) {
should be changed as as well, as the number of valid lines might not match the number of expected lines.
Don't forget to free your memory after you are done with it.
These two lines quickly create a memory leak (original pointer value of words[i] is lost), and then set up a use-after-free bug, since the new value of words[i] is the same pointer value as words[lowest], which is freed.
words[i] = words[lowest];
free(words[lowest]);
There is no need for the extra buffer, the (de)allocations, and the string copying here. Just swap the pointer values like you would if you were sorting an array of integers, for example.
char *tmp = words[i];
words[i] = words[lowest];
words[lowest] = tmp;
Here is a common cursory example, without error handling, using strdup. It should illustrate swapping pointer values, and determining the length of the array.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LINES 256
int main(void) {
char line[4096];
char **lines = malloc(sizeof *lines * MAX_LINES);
size_t len = 0;
while (len < MAX_LINES && fgets(line, sizeof line, stdin))
lines[len++] = strdup(line);
for (size_t i = 0; i < len - 1; i++) {
size_t lowest = i;
for (size_t j = i + 1; j < len; j++)
if (strcmp(lines[j], lines[lowest]) < 0)
lowest = j;
if (lowest != i) {
char *tmp = lines[i];
lines[i] = lines[lowest];
lines[lowest] = tmp;
}
}
for (size_t i = 0; i < len; i++) {
printf("%s", lines[i]);
free(lines[i]);
}
free(lines);
}
stdin:
one
two
hello
foo
world
^D
stdout:
foo
hello
one
two
world

What am I doing wrong with malloc and realloc of array of struct?

I'm trying to build in C an array of structures without defining the length of the maximum size of the array.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct text {
char *final;
} text;
int main() {
int n, sizearray = 10, i;
char *str;
text *testo;
testo = (text *)malloc(sizeof(text) * sizearray);
fgets(str, 1024, stdin);
i = 0;
while (str[0] != 'q') {
if (i == sizearray - 1) {
testo = (text *)realloc(testo, sizearray * 2 * sizeof(text));
}
n = strlen(str);
n = n + 1;
testo[i].finale = (char *)malloc(sizeof(char) * n);
strcpy(testo[i].finale, str);
i++;
fgets(str, 1024, stdin);
}
for (i = 0; i < sizearray; i++)
printf("%s \n", testo[i].finale);
return 0;
}
this gives me
process finished with exit code 139 (interrupted by signal 11:SIGSEV).
What am I doing wrong?
There are multiple issues in your code:
[major] str is an uninitialized pointer. You should make it an array of char defined with char str[1024].
[major] you do not adjust sizearray when you double the size of the array, hence you will never reallocate the array after the initial attempt at i = 9.
[major] the final loop goes to sizearray but there are potentially many uninitialized entries at the end of the array. You should stop at the last entry stored into the array.
you should also check the return value of fgets() to avoid an infinite loop upon premature end of file.
you should test for potential memory allocation failures to avoid undefined behavior.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct text {
char *finale;
} text;
int main() {
char str[1024];
text *testo = NULL;
size_t sizearray = 0;
size_t i, n = 0;
while (fgets(str, sizeof str, stdin) && *str != 'q') {
if (n == sizearray) {
/* increase the size of the array by the golden ratio */
sizearray += sizearray / 2 + sizearray / 8 + 10;
testo = realloc(testo, sizearray * sizeof(text));
if (testo == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
}
testo[n].finale = strdup(str);
if (testo[n].finale == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
n++;
}
for (i = 0; i < n; i++) {
printf("%s", testo[i].finale);
}
for (i = 0; i < n; i++) {
free(testo[i].finale);
}
free(testo);
return 0;
}
str is uninitialized. Either allocate memory with malloc or define it as an array with char str[1024].

word scramble with pointers in an array. Crashes when run

I am new to arrays with pointers, and I am trying to make an array of pointers word scramble game that allows 3 tries to guess the word before the game ends. Basically, I have created a function that scrambles a string. Then, that string is sent to a new string, which is shown to the user. The user then enters their guess. I am getting no signal from my compiler on what is wrong.. It just crashes when it is run. I believe the error is when I am sending the pointer to the method. Could someone please tell me why this error is happening? Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
void scramble(char *strings)
{
int length = strlen(strings), i, randomNum;
char temp;
for(i = 0; i < length/2; i++)
{
randomNum = rand()%length;
temp = strings[i];
strings[i] = strings[length - randomNum];
strings[length - randomNum] = temp;
}
}
int main()
{
int i, tries, NUMWORDS;
char *words[] = { "pumpkin", "cantalope", "watermelon", "apple", "kumquat" };
char *scramWords, *user;
NUMWORDS = strlen(words);
srand(time(NULL));
for(i = 0; i < NUMWORDS; i++)
{
scramWords[i] = words[i];
scramble(scramWords[i]);
}
printf("How to play: You get 3 tries to guess each scrambled word.\n");
for(i = 0; i < NUMWORDS; i++)
{
tries = 0;
while(tries !=4)
{
if(tries == 3)
{
printf("You Lose\n");
return 0;
}
printf("Unscramble: %s\n", scramWords[i]);
gets(user);
if(strcmp(user, words[i]) == 0)
{
printf("Correct!\n");
break;
}
else
{
tries++;
}
}
}
printf("You Win!");
return 0;
}
You must not try to modify string literals, or you will invoke undefined behavior. Copy strings before editing them instead of just assigning pointers.
length - randomNum may be length when randomNum is 0.
strlen(words) won't be the number of elements in words. You can use sizeof(words) / sizeof(*words).
You must allocate some buffer to scramWords and user before writing anything there.
You shouldn't use gets(), which has unavoidable risk of buffer overrun, deprecated in C99 and removed from C11.
Try this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
void scramble(char *strings)
{
int length = strlen(strings), i, randomNum;
char temp;
for(i = 0; i < length/2; i++)
{
randomNum = rand()%length;
temp = strings[i];
strings[i] = strings[length - randomNum - 1];
strings[length - randomNum - 1] = temp;
}
}
int main(void)
{
int i, tries, NUMWORDS;
char *words[] = { "pumpkin", "cantalope", "watermelon", "apple", "kumquat" };
char **scramWords, user[1024], *lf;
NUMWORDS = sizeof(words) / sizeof(*words);
srand(time(NULL));
scramWords = malloc(sizeof(*scramWords) * NUMWORDS);
if(scramWords == NULL)
{
perror("malloc");
return 1;
}
for(i = 0; i < NUMWORDS; i++)
{
scramWords[i] = malloc(strlen(words[i]) + 1); /* +1 for terminating null-character */
if(scramWords[i] == NULL)
{
perror("malloc");
return 1;
}
strcpy(scramWords[i], words[i]);
scramble(scramWords[i]);
}
printf("How to play: You get 3 tries to guess each scrambled word.\n");
for(i = 0; i < NUMWORDS; i++)
{
tries = 0;
while(tries !=4)
{
if(tries == 3)
{
printf("You Lose\n");
return 0;
}
printf("Unscramble: %s\n", scramWords[i]);
if(fgets(user, sizeof(user), stdin) == NULL)
{
puts("fgets failed");
return 1;
}
if((lf = strchr(user, '\n')) != NULL)
{
*lf = '\0'; /* remove newline character after string read */
}
if(strcmp(user, words[i]) == 0)
{
printf("Correct!\n");
break;
}
else
{
tries++;
}
}
}
printf("You Win!");
return 0;
}
you have a few issues in your code:
1), scramblegets a char * but here
scramWords[i] = words[i];
scramble(scramWords[i]);
you provide it with a char so define your scramWords as a char** instead of char*
2) You don't allocate space when declaring a pointer - that could lead to segfault. Use malloc or before accessing the pointer.
3) When assigning strings from one pointer to another use strcpy, not = operator
4) Use sizeof(words)/sizeof(*words) instead of NUMWORDS = strlen(words);
That should leave you with a working piece of code, but, as said in comments - take care of your warnings!

Splint: "Value strings[] used before definition" with dynamic array

I'm using a dynamic array of strings in C:
char** strings;
I initialize it:
int max = 10;
strings = malloc(sizeof(char*) * max);
And copy a couple of dummy strings:
char* str = "dummy";
for (int i = 0; i < max; i++) {
strings[i] = malloc(strlen(str) + 1);
strncpy(strings[i], str, strlen(str) + 1);
}
Yet when I try to print this:
for (int i = 0; i < max; i++)
printf("array = %s", strings[i])
I get this error from Splint:
Value strings[] used before definition
An rvalue is used that may not be initialized to a value on some execution
path. (Use -usedef to inhibit warning)
Checking for NULL like this will not help:
for (int i = 0; i < max; i++)
if (strings[i] != NULL)
printf("array = %s", strings[i])
since strings[i] is still used "before definition".
Any ideas on how to solve this?
Edit: Will try this with a linked list instead, I think.
Also, complete code listing:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
char** strings;
int i;
int max = 10;
char* str = "hello";
// Dynamic array with size max
strings = malloc(sizeof(char*) * max);
// Abort if NULL
if (strings == NULL)
return (-1);
// Define strings
for (i = 0; i < max; i++)
{
strings[i] = malloc(strlen(str) + 1);
// Abort if NULL
if (strings[i] == NULL)
{
// Undetected memory leak here!
free(strings);
return (-1);
}
strncpy(strings[i], str, strlen(str) + 1);
}
// Print strings
for (i = 0; i < max; i++)
{
if (strings[i] != NULL)
printf("string[%d] = %s\n", i, strings[i]);
}
// Free strings
for (i = 0; i < max; i++)
{
if (strings[i] != NULL)
free(strings[i]);
}
free(strings);
return 0;
}
I do not have Splint on my machine, so i cannot test with it, just an another way to your task:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main()
{
int i, len, max;
char* str = "hello";
len = strlen(str) + 1;
max = 10;
char strings[max][len];
for (i = 0; i < max; i++) {
strcpy(strings[i], str);
}
for (i = 0; i < max; i++) {
printf("string[%d] = %s\n", i, strings[i]);
}
return 0;
}
Avoid creating non-continuous memory it would be better approach if you allocate memory in single malloc call.
Memory can be freed in single free call instead of multiple free call
max_rows * sizeof(char) will allocate 2 * 1
((strlen(str) * N) + 1) will allocate memory for every N element.
Here is my approch
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
size_t max_rows = 2;
char* str = "dummpy";
char* vec_s = (char *) malloc( max_rows * sizeof(char) * ((strlen(str) * max_rows) + 1));
for (int i = 0; i < max_rows; i++){
strcpy((vec_s + i), str);
printf("vec_s[%d]=%s\n", i, (vec_s + i));
}
free(vec_s);
return 0;
}

Resources