I have to write a program that sorts quotes by length in a text file and outputs the result to another file. But while the compile works and builds with no error, I still get unexpected results, plus a run error in netbeans:
void read_in(char** quotes, size_t* size){
*size = 0;
FILE *filePointer;
filePointer = fopen("quotes.txt", "r");
if (filePointer == NULL){
perror("Error: File cannot be opened! \n");
exit(1);
}
char tempArr[MAX_LEN];
while(fgets(tempArr, sizeof(tempArr), filePointer)){
if(*size == MAX_QUOTES){
printf("Warning: File contains more than 7 quotes. Load halted. \n");
return;
}
char* ptrMem = (char*)malloc(sizeof(char) * strlen(tempArr));
if(!ptrMem){
printf("Error: Memory could not be allocated! \n");
return;
}
strcpy(ptrMem, tempArr);
ptrMem[strcspn(ptrMem, "\n")] = '\r';
quotes[(*size)++] = ptrMem;
}
fclose(filePointer);
}
//-----------------------------------------------------------------------------------------------
void selection_sort(char **quotes, size_t size){
// Current min value to compare
int minValue;
printf("--- Input:\n");
// Loop and print quotes from array
for(int i = 0; i < (size - 1); i++){
printf("%s\n", quotes[i]);
}
for(int i = 0; i < (size - 1); i++){
// Determine minimum element
minValue = i ;
for(int j = i + 1; j < size; j++) {
if(strlen(quotes[j]) < strlen(quotes[minValue])){
minValue = j;
}
swap("es[minValue], "es[i]);
}
}
}
//-----------------------------------------------------------------------------------------------
void print_out(char** quotes, size_t size){
printf("--- Output:\n");
for (int i = 0; i < size; i++){
printf("%s\n", quotes[i]);
}
}
//-----------------------------------------------------------------------------------------------
void write_out(char** quotes, size_t size){
FILE *filePointer = fopen("output.txt", "w");
// If file ptr null value, throw exception
if (filePointer == NULL){
perror("Error: Cannot write to file!");
exit(1);
}
for (int i = 0; i < size; i++){
fprintf(filePointer, "%s\n", quotes[i]);
}
// Close file ptr after use
fclose(filePointer);
}
//-----------------------------------------------------------------------------------------------
void free_memory(char** quotes, size_t size){
// Loop through array of quotes (pointers) and free each allocation
for (int i = 0; i < size ;i++){
free(quotes[i]);
}
}
//-----------------------------------------------------------------------------------------------
void swap(char **quote_A, char **quote_B){
char *temp = *quote_B;
*quote_A = *quote_B;
*quote_A = temp;
}
This is what I get in ouput:
For some reason, it's repeating a string (char pointer quote) over and over. What it's supposed to do is have them sorted by length which is what my selection_sort algorithm was supposed to do. All the function prototypes were defined above that code. I figured it would take too much space to put the entire program, but I can if needed.
Also, the main method and header files are defined above.
EDIT: I do get some warning about strcpy not being able to be limited to a max buffer size
Related
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
Sort command of linux must sort the lines of a text file and transfer the output to another file. But my code gives a runtime error. Please rectify the pointer mistakes so that output.
In which line exactly should I make changes? Because there is no output after all.
I'm pasting the whole code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void sortfile(char **arr, int linecount) {
int i, j;
char t[500];
for (i = 1; i < linecount; i++) {
for (j = 1; j < linecount; j++) {
if (strcmp(arr[j - 1], arr[j]) > 0) {
strcpy(t, arr[j - 1]);
strcpy(arr[j - 1], arr[j]);
strcpy(arr[j], t);
}
}
}
}
int main() {
FILE *fileIN, *fileOUT;
fileIN = fopen("test1.txt", "r");
unsigned long int linecount = 0;
int c;
if (fileIN == NULL) {
fclose(fileIN);
return 0;
}
while ((c = fgetc(fileIN)) != EOF) {
if (c == '\n')
linecount++;
}
printf("line count=%d", linecount);
char *arr[linecount];
char singleline[500];
int i = 0;
while (fgets(singleline, 500, fileIN) != NULL) {
arr[i] = (char*)malloc(500);
strcpy(arr[i], singleline);
i++;
}
sortfile(arr, linecount);
for (i = 0; i < linecount; i++) {
printf("%s\n", arr[i]);
}
fileOUT = fopen("out.txt", "w");
if (!fileOUT) {
exit(-1);
}
for (i = 0; i < linecount; i++) {
fprintf(fileOUT, "%s", arr[i]);
}
fclose(fileIN);
fclose(fileOUT);
}
The problem in your code is you do not rewind the input stream after reading it the first time to count the number of newlines. You should add rewind(fileIN); before the next loop.
Note however that there are other problems in this code:
the number of newline characters may be less than the number of successful calls to fgets(): lines longer than 499 bytes will be silently broken in multiple chunks, causing more items to be read by fgets() than newlines. Also the last line might not end with a newline. Just count the number of successful calls to fgets().
You allocate 500 bytes for each line, which is potentially very wasteful. Use strdup() to allocate only the necessary size.
Swapping the lines in the sort routine should be done by swapping the pointers, not copying the contents.
allocating arr with malloc is safer and more portable than defining it as a variable sized array with char *arr[linecount];
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void sortfile(char **arr, int linecount) {
for (;;) {
int swapped = 0;
for (int j = 1; j < linecount; j++) {
if (strcmp(arr[j - 1], arr[j]) > 0) {
char *t = arr[j - 1];
arr[j - 1] = arr[j];
arr[j] = t;
swapped = 1;
}
}
if (swapped == 0)
break;
}
}
int main() {
FILE *fileIN, *fileOUT;
char singleline[500];
int i, linecount;
fileIN = fopen("test1.txt", "r");
if (fileIN == NULL) {
fprintf(stderr, "cannot open %s\n", "test1.txt");
return 1;
}
linecount = 0;
while (fgets(singleline, 500, fileIN)) {
linecount++;
}
printf("line count=%d\n", linecount);
char **arr = malloc(sizeof(*arr) * linecount);
if (arr == NULL) {
fprintf(stderr, "memory allocation failure\n");
return 1;
}
rewind(fileIN);
for (i = 0; i < linecount && fgets(singleline, 500, fileIN) != NULL; i++) {
arr[i] = strdup(singleline);
if (arr[i] == NULL) {
fprintf(stderr, "memory allocation failure\n");
return 1;
}
}
fclose(fileIN);
if (i != linecount) {
fprintf(stderr, "line count mismatch: i=%d, lilnecount=%d\n",
i, linecount);
linecount = i;
}
sortfile(arr, linecount);
for (i = 0; i < linecount; i++) {
printf("%s", arr[i]);
}
fileOUT = fopen("out.txt", "w");
if (!fileOUT) {
fprintf(stderr, "cannot open %s\n", "out.txt");
return 1;
}
for (i = 0; i < linecount; i++) {
fprintf(fileOUT, "%s", arr[i]);
}
fclose(fileOUT);
for (i = 0; i < linecount; i++) {
free(arr[i]);
}
free(arr);
return 0;
}
To get a different sort order, you would change the comparison function. Instead of strcmp() you could use this:
#include <ctype.h>
int my_strcmp(const char *s1, const char *s2) {
/* compare strings lexicographically but swap lower and uppercase letters */
unsigned char c, d;
while ((c = *s1++) == (d = *s2++)) {
if (c == '\0')
return 0; /* string are equal */
}
/* transpose case of c */
if (islower(c)) {
c = toupper(c);
} else {
c = tolower(c);
}
/* transpose case of d */
if (islower(d)) {
d = toupper(d);
} else {
d = tolower(d);
}
/* on ASCII systems, we should still have c != d */
/* return comparison result */
if (c <= d)
return -1;
} else {
return 1;
}
}
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;
}
I have created a program that reads a series of strings from a .txt file and after compiling a new .txt file is created where the strings should be in alphabetical order.The problem is that I can't write more than 10 words, the compiler just stops/crashes, WHY? Does it depend by the type of compiler? I am currently using Code-Bloks.How can I optimize the code to run more smoothly?
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
void arrange(int n, char *x[])
{
char *temp;
int i,str;
for(str = 0; str < n-1; ++str)
{
for(i = str+1; i < n; ++i)
{
if(strcmp(x[str],x[i]) > 0)
{
temp = x[str];
x[str] = x[i];
x[i] = temp;
}
}
}
return;
}
int number_of_lines = 0;
void countOfLinesFromFile(char *filename){
FILE* myfile = fopen(filename, "r");
int ch;
do
{
ch = fgetc(myfile);
if(ch == '\n')
number_of_lines++;
}
while (ch != EOF);
if(ch != '\n' && number_of_lines != 0)
number_of_lines++;
fclose(myfile);
return number_of_lines;
}
int main()
{
int i , ts=0;
char *x[10];
char *fileName = "WORDS.txt";
countOfLinesFromFile(fileName);
printf("%d",number_of_lines);
FILE * fp;
fp = fopen ("WORDS.txt", "r");
for(i = 0; i < number_of_lines; i++)
{
x[i] = (char*) malloc (1200*sizeof(char));
fscanf(fp, "%s", x[i]);
}
FILE *fPointer;
fPointer=fopen("Alphabetical.txt","w+");
arrange(i,x);
for(i = 0; i < number_of_lines; i++)
{
fprintf(fPointer,"%s\n",x[i]);
}
fclose(fPointer);
fclose(fp);
return 0;
}
char *x[10];
The buffer size is too small
These two lines define how much information you can store
char *x[10]; // 10 strings
x[i] = (char*) malloc (1200*sizeof(char)); // 1200 characters each
As it is written now, you can only hold a maximum of 10 strings with each string being no longer than 1200 characters.
The crash is caused when number_of_lines >= 11 in the following for loop:
for(i = 0; i < number_of_lines; i++)
{
x[i] = (char*) malloc (1200*sizeof(char));
fscanf(fp, "%s", x[i]);
}
When i is 11 you write to x[11] which is past the end of x.
#include <stdio.h>
#include <stdlib.h>
int main() {
int *width;
int *height;
int row;
int column;
int character;
int count;
int pictureit;
double i = 0;
FILE *fp;
char file[50];
char line[25]; // assume each line has max 25 characters
printf("What file should we pull from: ");
scanf("%s", file);
//read file using File pointer
fp = fopen(file, "r");
// read the first line in the file
fgets(line, sizeof(line), fp);
width = strtok(line,"x");
height = strtok(NULL, "/0");
// read all the future lines in the file excluding the first
while (fgets(line, sizeof(line), fp)) {
row = strtok(line, ",");
column = strtok(NULL, ",");
character = strtok(NULL, ",");
count = strtok(NULL, "/0");
if(i < count) {
**printf("%s", pictureit[row][column] = character);**
i++;
}
}
fclose(fp);
return 0;
}
I'm pulling in a file with this kind of setup
75x53
0,36,.,1
0,37,M,1
1,32,.,1
1,33,:,1
1,34,A,1
1,35,M,2
1,37,O,1
1,38,:,1
2,23,.,1
2,24,:,1
2,25,A,1
2,26,M,5
I've been brainstorming for a while. How would I go about displaying this on the console? It obviously needs to go into a 2d array. The program needs to know the height and width of the array to display a space or character in that spot.
PS: This program will display a picture in the console when finished. The "** **" is where I am working.
You could dynamically allocate a 2d array with the right dimensions (according to your first line), then fill it up with the data from your file and finally print it with two nested for loops.
EDIT: Basically, you would do:
//...
//Create the dynamic array
char ** array = malloc(sizeof(char) * height);
int i;
for(i = 0; i < height; i++)
array[i] = malloc(sizeof(char) * width);
// Fill your array here (put different chars in it) ...
//Print it
int x,y;
for(y = 0; y < height; y++)
{
for(x = 0; x < width; x++)
printf("%c ", array[y][x]);
printf("\n");
}
//Free the array
for(i = 0; i < height; i++)
free(array[i]);
free(array);
I voluntarily omitted the part where you check the return value of the malloc (whether it's NULL or not), but you should definitely add it.
Normally I wouldn't do this, but I felt the need to do a scanning exercise:
int main(void)
{
char fn[100];
fprintf(stdout, "Enter file name:");
fflush(stdout);
int result = fscanf(stdin, " %99s", fn);
if (1 != result)
{
fprintf(stderr, "Reading the file's name failed.\n");
exit(EXIT_FAILURE);
}
{
size_t width= 0;
size_t height 0;
FILE * pf = fopen(fn, "r");
if (NULL == pf)
{
fprintf(stderr, "Opening file '%s' failed.\n", fn);
exit(EXIT_FAILURE);
}
{
result = fscanf(pf, " %zux%zu", &width, &height);
if (2 != result)
{
fprintf(stderr, "Reading width and/or heigth from file '%s' failed.\n", fn);
exit(EXIT_FAILURE);
}
{
char (*pa)[width][height] = calloc(1, sizeof *pa);
if (NULL == pa)
{
perror("calloc() failed");
exit(EXIT_FAILURE);
}
{
size_t number_of_rows = width * height;
fprintf(stderr, "Trying to read %zu data rows.\n", number_of_rows);
for (size_t row = 0; row < number_of_rows; ++row)
{
size_t x, y;
char c;
int i;
result = fscanf(pf, " %zu,%zu,%c,%d", &x, &y, &c, &i);
if (4 != result)
{
fprintf(stderr, "Reading data (#%zu) row from '%s' failed.\n", row, fn);
exit(EXIT_FAILURE);
}
/* Add check here to avoid accessing the array out-of-bounds! */
(*pa)[x][y] = c;
}
}
{
for (size_t row = 0; row < width; ++row)
{
for (size_t column = 0; column < height; ++column)
{
fprintf(stdout, "%c", (*pa)[row][column]);
}
fprintf(stdout, "\n");
}
}
free(pa);
}
}
fclose(pf);
}
return EXIT_SUCCESS;
}
Also I am curious about the picture to be printed ... ;-)