The following program prints the word frequency in a file. I am trying to save for each word in the text file, which line/lines appeared and how many times in total in the whole file. It counts how many times it appeared but I can't get which lines of the txt file.
There is a problem with int lines[]. The ouput of the program gives
segmentation fault
#define MAXWORDS 10000
#define MAXSTRING 100
/* structure holding word frequency information */
typedef struct _word {
char s[MAXSTRING]; /* the word */
int count; /* number of times word occurs */
int lines[1000];
} word;
void insert_word (word *words, int *n, char *s, int no) {
int i;
/* linear search for the word */
for (i=0; i<*n; i++) if (strcmp (s, words[i].s) == 0) {
/* found it? increment and return. */
words[i].count++;
words[i].lines[words[i].count]=no;
printf("%d", no);
return;
}
/* error conditions... */
if (strlen (s) >= MAXSTRING) {
fprintf (stderr, "word too long!\n");
exit (1);
}
if (*n >= MAXWORDS) {
fprintf (stderr, "too many words!\n");
exit (1);
}
/* copy the word into the structure at the first available slot,
* i.e., *n
*/
strcpy (words[*n].s, s);
/* this word has occured once up to now, so count = 1 */
words[*n].count = 1;
words[*n].lines[words[*n].count]=no;
/* one more word */
(*n)++;
}
...........
int main () {
word words[MAXWORDS];
char s[1000];
int i, n, m;
n = 0;
FILE* file = fopen("test.txt", "r");
/* read all the words in the file... */
int no=1;
while (fgets(s, sizeof(s), file)) {
scanf ("%s", s);
insert_word (words, &n, s,no);
no=no+1;
}
}
fclose(file);
qsort((void *) words, n, sizeof (word),
(int (*) (const void *, const void *)) wordcmp);
if (n < 20)
m = n;
else
m = 20;
for (i=0; i<m; i++)
printf ("%s\t[%d] {%d} \n", words[i].s, words[i].count, words[i].lines);
}
Here's a solution with dynamic memory allocation:
typedef struct _word {
char *s; /* the word */
int count; /* number of times word occurs */
int *line_numbers; // Array of line numbers
int num_line_numbers; // Size of the array of line numbers
} word;
// Creating a struct to hold the data. I find it's easier
typedef struct {
word *words; // The array of word structs
int num_words; // The size of the array
} word_list;
void insert_word (word_list *words, char *s, int line_number)
{
/* linear search for the word */
for (int i = 0; i < words->num_words; i++) {
if (strcmp (s, words->words[i].s) == 0) {
/* found it? increment and return. */
words->words[i].count++;
// See if it already appeared in this line
if (words->words[i].line_numbers[words->words[i].num_line_numbers - 1] == line_number) {
return;
}
// New line number. Increase the line number array by one
int *tmp = realloc(words->words[i].line_numbers, sizeof(int) * (words->words[i].num_line_numbers + 1));
if (NULL == tmp) exit(0);
words->words[i].line_numbers = tmp;
// Add the line number to the array
words->words[i].line_numbers[words->words[i].num_line_numbers] = line_number;
words->words[i].num_line_numbers += 1;
return;
}
}
/* error conditions... */
....
// Increase the size of the word array by one.
word *tmp = realloc(words->words, sizeof(word) * (words->num_words + 1));
if (tmp == NULL) exit(0);
words->words = tmp;
/* copy the word into the structure at the first available slot,
* i.e., *n
*/
words->words[words->num_words].s = malloc(strlen(s) + 1);
strcpy(words->words[words->num_words].s, s);
/* this word has occurred once up to now, so count = 1 */
words->words[words->num_words].count = 1;
words->words[words->num_words].line_numbers = malloc(sizeof(int));
words->words[words->num_words].line_numbers[0] = line_number;
words->words[words->num_words].num_line_numbers = 1;
words->num_words += 1;
}
bool remove_word(word_list *words, const char *word_to_delete)
{
for (int i = 0; i < words->num_words; i++) {
if (0 == strcmp(words->words[i].s, word_to_delete)) {
// TODO: handle special case where there is only 1 word in list
// Calc number of words after found word
int number_of_words_to_right = words->num_words - i - 1;
// Free mem
free(words->words[i].s);
free(words->words[i].line_numbers);
// Copy remaining words
memcpy(&words->words[i], &words->words[i + 1], sizeof(word) * number_of_words_to_right);
// Resize the array (technically not required)
word *tmp = realloc(words->words, sizeof(word) * --words->num_words);
if (NULL == tmp) exit(0);
words->words = tmp;
return true;
}
}
return false;
}
And in main()
word_list *words = malloc(sizeof(word_list));
if (NULL == words) exit(0);
memset(words, 0, sizeof(word_list));
....
/* read all the words in the file... */
char s[1000];
int line_number = 1;
while (fgets(s, sizeof(s), file)) {
char *word = strtok(s, " ");
while (word != NULL) {
size_t len = strlen(word);
if (len > 0 && word[len - 1] == '\n') word[--len] = 0;
insert_word(words, word, line_number);
word = strtok(NULL, " ");
}
line_number += 1;
}
fclose(file);
for (int i = 0; i < words->num_words; i++) {
printf("%s\t\t[%d] {", words->words[i].s, words->words[i].count);
for (int j = 0; j < words->words[i].num_line_numbers; j++) {
if (j != 0) printf(",");
printf("%d", words->words[i].line_numbers[j]);
}
printf("}\n");
}
// It's good practice to always free mem. It's super not important
// in this app since the OS will do it when you exit
for (int i = 0; i < words->num_words; i++) {
free(words->words[i].s);
free(words->words[i].line_numbers);
}
free(words->words);
free(words);
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
I'm working on a school project where I have to store PPM data into structs. I have an issue with an array of strings inside the struct.
typedef struct {
char **comments;
} PPM;
I have 3 functions that uses this struct.
PPM *getPPM() is used to get all the PPM data from file and store it into the struct
void showPPM(PPM * img) to show the image data in terminal
PPM *encode(PPM *img) which is used to change the LSB of the RGB values of the image
The problem is that getPPM works as intended and gets all the comments into the comments array in getPPM. It displays them fine if I do it like this:
PPM *p = getPPM(fin);
showPPM(p);
But if I try to call it with the encode function like this:
PPM *p = getPPM(fin);
PPM *g = encode(p);
showPPM(g);
the debugger shows that as soon as the program enters the encode function, the comments value resets to NULL even though this function doesn't even touch comments. Is the way I am calling these functions wrong or is there something wrong with my code? I will try to provide the minimal code if the problem is not the way the functions are being called, as the code is big and everything is dependent on one another.
I'm very new to C language. I tried understanding the problem for hours but can't find a solution anywhere. Any help would be greatly appreciated.
EDIT: This is as small as I could make it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Structures
typedef struct {
int r, g, b;
} pixels;
typedef struct {
char format[3];
char **comments;
int width, height, maxColors, commentCounter;
pixels **pixelValues;
} PPM;
// Functions declarations
PPM *getPPM(FILE * f);
PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret);
void showPPM(PPM * im);
static int *decimalToBinary(const char *message, unsigned int length);
// Main method
int main(int argc, char **argv) {
FILE * fin = fopen(argv[1], "r");
if(fin == NULL) {
perror("Cannot open file");
exit(1);
}
PPM *p = getPPM(fin);
PPM *g = encode(p, "test", 5, 1);
showPPM(g);
return 0;
}
/*
* This function is used to get the image data from a file and populate
* our strutures with it.
*/
PPM *getPPM(FILE * f) {
// Allocate the memory for structure and check if it was successful
PPM *pic = (PPM *) malloc(sizeof(PPM));
if(!pic) {
perror("Unable to allocate memory for structure");
exit(1);
}
char line[100]; // Expecting no more than 100 characters per line.
pic->commentCounter = 0; // This is the counter to keep size if there are more than one comments
int pixelsCounter = 0; // Counter for pixels' array
pic->comments = malloc(sizeof(char *));
pic->pixelValues = malloc(sizeof(PPM));
int lineCounter = 0;
if((pic->comments) == NULL) {
perror("Unable to allocate memory for pixels");
exit(1);
}
while(fgets(line, sizeof(line), f)) {
// Reference: https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input
size_t length = strlen(line);
if(length > 0 && line[length-1] == '\n') {
line[--length] = '\0';
}
// Assigning the file format
if(line[0] == 'P') {
pic->format[0] = line[0];
pic->format[1] = line[1];
pic->format[2] = '\0';
}
//Populate comments into struct PPM
if(line[0] == '#') {
// Reallocate/allocate the array size each time a new line of comment is found
if(pic->commentCounter != 0) {
pic->comments = realloc(pic->comments, (pic->commentCounter+1) * sizeof(char *));
}
// Allocate the memory for the string
pic->comments[pic->commentCounter] = malloc(100 * sizeof(char));
// Write the at commentCounter position of the array; character by character
int i = 0;
while(line[i] != '\0') {
pic->comments[pic->commentCounter][i] = line[i];
i++;
}
pic->comments[pic->commentCounter][i] = '\0';
pic->commentCounter++;
}
/*
* Loading the max color property of the file which is gonna be 3 letters (Usually 255)
* and checking if we previously got maxColors in our construct or not. If we didn't
* then we load this value into the consturct and the condition will never validate
* throughout the iterations
*/
if(strlen(line) == 3 && pic->maxColors == 0 && line[0] != '#') {
pic->maxColors = atoi(line);
continue;
}
/*
* Check if the length of string > 3, which means it is going to be a
* number, potentially RGB value or a comment. But since width & height
* comes before RGB values, our condition will fail once we have found
* the width/height for the next iteration. That's why this condition
* only checks if it is a comment or a numbered value of length > 3
*/
if((strlen(line) > 3) && (pic->width == 0) && (line[0] != '#')) {
char *width = strtok(line, " ");
char *height = strtok(NULL, " ");
pic->width = atoi(width);
pic->height = atoi(height);
continue;
}
/*
* If the width/height and maxColors have been found, that means every
* other line is either going to be the RGB values or a comment.
*/
if((pic->width != 0) && (pic->maxColors != 0) && (line[0] != '#')) {
// length(line) > 3 means all the RGB values are in same line
if(strlen(line) > 3) {
char *val1 = strtok(line, " ");
char *val2 = strtok(NULL, " ");
char *val3 = strtok(NULL, " ");
// pixelsCounter = 0 means it's the first element.
if(pixelsCounter != 0) {
// Reallocate memory each time a new R G B value line is found
pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
}
pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
pic->pixelValues[pixelsCounter]->r = atoi(val1);
pic->pixelValues[pixelsCounter]->g = atoi(val2);
pic->pixelValues[pixelsCounter]->b = atoi(val3);
pixelsCounter++;
} else if(strlen(line) <= 3) {
/*
* If each individual RGB values are in a separete lines, we will
* use a switch case and a line counter to keep track of where the
* values were inserted and when to know when we got RGB values for
* one pixel
*/
if(pixelsCounter != 0 && lineCounter == 0) {
// Reallocate memory each time a new R G B value line is found
pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
}
switch(lineCounter) {
case 0 :
pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
pic->pixelValues[pixelsCounter]->r = atoi(line);
lineCounter++;
continue;
case 1 :
pic->pixelValues[pixelsCounter]->g = atoi(line);
lineCounter++;
continue;
case 2 :
pic->pixelValues[pixelsCounter]->b = atoi(line);
lineCounter=0;
pixelsCounter++;
continue;
default:
continue;
}
}
}
}
pic->pixelValues[pixelsCounter] = NULL;
fclose(f);
return pic;
}
void showPPM(PPM * im) {
printf("%s\n",im->format);
int k = 0;
while(k < im->commentCounter) {
printf("%s\n", im->comments[k]);
k++;
}
printf("%d %d\n", im->width, im->height);
printf("%d\n",im->maxColors);
int j = 0;
while(im->pixelValues[j] != NULL) {
printf("%d %d %d\n", im->pixelValues[j]->r, im->pixelValues[j]->g, im->pixelValues[j]->b);
j++;
}
}
PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret) {
int *binaryMessage = decimalToBinary(message, mSize);
int i, j = 0, lineCounter = 0;
for(i = 0; i < 40; i++) {
switch(lineCounter) {
case 0 :
im->pixelValues[j]->r |= binaryMessage[i] << 0;
lineCounter++;
continue;
case 1 :
im->pixelValues[j]->g |= binaryMessage[i] << 0;
lineCounter++;
continue;
case 2 :
im->pixelValues[j]->b |= binaryMessage[i] << 0;
lineCounter=0;
j++;
continue;
default:
continue;
}
}
return im;
}
/*
* Converts a string into binary to be used in encode function. It
* first converts each letter of the string into ascii code. Then
* finds and stores each of the 8 bits of that int (ascii code of
* the letter) sequentially in an array.
*/
static int *decimalToBinary(const char *message, unsigned int length) {
/*
* malloc is used here instead of [] notation to allocate memory,
* because defining the variable with [] will make its scope
* limited to this function only. Since we want to access this
* array later on, we use malloc to assign space in the memory
* for it so we can access it using a pointer later on.
*/
int k=0, i, j;
unsigned int c;
unsigned int *binary = malloc(8 * length);
for(i = 0; i < length; i++) {
c = message[i];
for(j = 7; j >= 0; j--,k++) {
/*
* We check here if the jth bit of the number is 1 or 0
* using the bit operator &. If it is 1, it will return
* 1 because 1 & 1 will be true. Otherwise 0.
*/
if((c >> j) & 1)
binary[k] = 1;
else
binary[k] = 0;
}
}
return binary;
}
PPM file:
P3
# CREATOR: GIMP PNM Filter Version 1.1
# Amazing comment 2
# Yet another amazing comment
400 530
255
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
0 0 0
in decimalToBinar
unsigned int *binary = malloc(8 * length);
must be
unsigned int *binary = malloc(8 * length * sizeof(int));
new code is :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//Structures
typedef struct {
int r, g, b;
} pixels;
typedef struct {
char format[3];
char **comments;
int width, height, maxColors, commentCounter;
pixels **pixelValues;
} PPM;
// Functions declarations
PPM *getPPM(FILE * f);
PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret);
void showPPM(PPM * im);
static int *decimalToBinary(const char *message, unsigned int length);
// Main method
int main(int argc, char **argv) {
FILE * fin = fopen(argv[1], "r");
if(fin == NULL) {
perror("Cannot open file");
exit(1);
}
PPM *p = getPPM(fin);
PPM *g = encode(p, "test", 5, 1);
showPPM(g);
free(p->comments);
free(p);
return 0;
}
/*
* This function is used to get the image data from a file and populate
* our strutures with it.
*/
PPM *getPPM(FILE * f) {
// Allocate the memory for structure and check if it was successful
PPM *pic = (PPM *) malloc(sizeof(PPM));
if(!pic) {
perror("Unable to allocate memory for structure");
exit(1);
}
char line[100]; // Expecting no more than 100 characters per line.
pic->commentCounter = 0; // This is the counter to keep size if there are more than one comments
int pixelsCounter = 0; // Counter for pixels' array
pic->comments = malloc(sizeof(char *));
pic->pixelValues = malloc(sizeof(PPM));
int lineCounter = 0;
if((pic->comments) == NULL) {
perror("Unable to allocate memory for pixels");
exit(1);
}
pic->width = 0;
pic->height = 0;
pic->maxColors = 0;
while(fgets(line, sizeof(line), f)) {
// Reference: https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input
size_t length = strlen(line);
if(length > 0 && line[length-1] == '\n') {
line[--length] = '\0';
}
// Assigning the file format
if(line[0] == 'P') {
pic->format[0] = line[0];
pic->format[1] = line[1];
pic->format[2] = '\0';
}
//Populate comments into struct PPM
if(line[0] == '#') {
// Reallocate/allocate the array size each time a new line of comment is found
if(pic->commentCounter != 0) {
pic->comments = realloc(pic->comments, (pic->commentCounter+1) * sizeof(char *));
}
// Allocate the memory for the string
pic->comments[pic->commentCounter] = malloc(100 * sizeof(char));
// Write the at commentCounter position of the array; character by character
int i = 0;
while(line[i] != '\0') {
pic->comments[pic->commentCounter][i] = line[i];
i++;
}
pic->comments[pic->commentCounter][i] = '\0';
pic->commentCounter++;
}
/*
* Loading the max color property of the file which is gonna be 3 letters (Usually 255)
* and checking if we previously got maxColors in our construct or not. If we didn't
* then we load this value into the consturct and the condition will never validate
* throughout the iterations
*/
if(strlen(line) == 3 && pic->maxColors == 0 && line[0] != '#') {
pic->maxColors = atoi(line);
continue;
}
/*
* Check if the length of string > 3, which means it is going to be a
* number, potentially RGB value or a comment. But since width & height
* comes before RGB values, our condition will fail once we have found
* the width/height for the next iteration. That's why this condition
* only checks if it is a comment or a numbered value of length > 3
*/
if((strlen(line) > 3) && (pic->width == 0) && (line[0] != '#')) {
char *width = strtok(line, " ");
char *height = strtok(NULL, " ");
pic->width = atoi(width);
pic->height = atoi(height);
continue;
}
/*
* If the width/height and maxColors have been found, that means every
* other line is either going to be the RGB values or a comment.
*/
if((pic->width != 0) && (pic->maxColors != 0) && (line[0] != '#')) {
// length(line) > 3 means all the RGB values are in same line
if(strlen(line) > 3) {
char *val1 = strtok(line, " ");
char *val2 = strtok(NULL, " ");
char *val3 = strtok(NULL, " ");
// pixelsCounter = 0 means it's the first element.
if(pixelsCounter != 0) {
// Reallocate memory each time a new R G B value line is found
pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
}
pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
pic->pixelValues[pixelsCounter]->r = atoi(val1);
pic->pixelValues[pixelsCounter]->g = atoi(val2);
pic->pixelValues[pixelsCounter]->b = atoi(val3);
pixelsCounter++;
} else if(strlen(line) <= 3) {
/*
* If each individual RGB values are in a separete lines, we will
* use a switch case and a line counter to keep track of where the
* values were inserted and when to know when we got RGB values for
* one pixel
*/
if(pixelsCounter != 0 && lineCounter == 0) {
// Reallocate memory each time a new R G B value line is found
pic->pixelValues = realloc(pic->pixelValues, (pixelsCounter + 1) * sizeof(PPM));
}
switch(lineCounter) {
case 0 :
pic->pixelValues[pixelsCounter] = malloc(12 * sizeof(pixels));
pic->pixelValues[pixelsCounter]->r = atoi(line);
lineCounter++;
continue;
case 1 :
pic->pixelValues[pixelsCounter]->g = atoi(line);
lineCounter++;
continue;
case 2 :
pic->pixelValues[pixelsCounter]->b = atoi(line);
lineCounter=0;
pixelsCounter++;
continue;
default:
continue;
}
}
}
}
pic->pixelValues[pixelsCounter] = NULL;
fclose(f);
return pic;
}
void showPPM(PPM * im) {
printf("%s\n",im->format);
int k = 0;
while(k < im->commentCounter) {
printf("%s\n", im->comments[k]);
k++;
}
printf("%d %d\n", im->width, im->height);
printf("%d\n",im->maxColors);
int j = 0;
while(im->pixelValues[j] != NULL) {
printf("%d %d %d\n", im->pixelValues[j]->r, im->pixelValues[j]->g, im->pixelValues[j]->b);
j++;
}
}
PPM *encode(PPM *im, char *message, unsigned int mSize, unsigned int secret) {
int *binaryMessage = decimalToBinary(message, mSize);
int i, j = 0, lineCounter = 0;
for(i = 0; i < 40; i++) {
switch(lineCounter) {
case 0 :
im->pixelValues[j]->r |= binaryMessage[i] << 0;
lineCounter++;
continue;
case 1 :
im->pixelValues[j]->g |= binaryMessage[i] << 0;
lineCounter++;
continue;
case 2 :
im->pixelValues[j]->b |= binaryMessage[i] << 0;
lineCounter=0;
j++;
continue;
default:
continue;
}
}
free(binaryMessage);
return im;
}
/*
* Converts a string into binary to be used in encode function. It
* first converts each letter of the string into ascii code. Then
* finds and stores each of the 8 bits of that int (ascii code of
* the letter) sequentially in an array.
*/
static int *decimalToBinary(const char *message, unsigned int length) {
/*
* malloc is used here instead of [] notation to allocate memory,
* because defining the variable with [] will make its scope
* limited to this function only. Since we want to access this
* array later on, we use malloc to assign space in the memory
* for it so we can access it using a pointer later on.
*/
int k = 0, i, j;
unsigned int c;
unsigned int *binary = malloc(8 * length * sizeof(int));
for(i = 0; i < length; i++) {
c = message[i];
for(j = 7; j >= 0; j--,k++) {
/*
* We check here if the jth bit of the number is 1 or 0
* using the bit operator &. If it is 1, it will return
* 1 because 1 & 1 will be true. Otherwise 0.
*/
if((c >> j) & 1)
binary[k] = 1;
else
binary[k] = 0;
}
}
return binary;
}
I'm allocating memory for my int *occurrences int *wordCounts and char **uniqueWords pointers and then at the end of the function that allocates the memory, i free them. However, when i compile the program i get an double free or corruption (!prev) aborting error. Is it caused by malloc,free or could it be due to how i initialize them inside the for loop ?
PS: I'm talking about the sortedCount() method, located towards the end
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#define MAX_STRING_SIZE 512 /* each line in the file can have up to 512 chars */
void populateWordsArray(int);
void reverse(int);
void first(int);
void middle(int);
void last(int);
int count(int, char*, int);
void sortedCount(int);
void determineUniqueWords(int *,char **, int);
void *malloc_or_end(size_t);
void* malloc_or_end(size_t sz) {
void *pointer;
pointer = malloc(sz);
if(pointer == NULL) {
printf("Out of memory, terminating.\n");
exit(-1);
}
return pointer;
}
/* turn into local */
FILE *file;
char **wordList;
void determineUniqueWords(int *occurrences, char **word, int N) {
int i = 0;
int j = 0;
for(i = 0; i < N; i++) {
if(occurrences[i] < 1) {
continue;
}
for(j = i + 1; j < N; j++) {
if(occurrences[j] == 1 && (strcmp(word[i],word[j])) == 0) {
occurrences[i]++;
occurrences[j] = 0;
}
}
}
}
/**
* Function populateWordsArray: reads N words from
* the given file and populates the wordList array with them.
* Has one argument: int N - the number of words to read.
* */
void populateWordsArray(int N) {
int i = 0;
while(i < N && (fscanf(file,"%s",wordList[i]) == 1)) { /* fscanf returns the number of successfully read items. If it's not 1, the read failed. Same as checking if fscanf reads the eof char. */
i++;
}
}
/**
* Function reverse: prints the words of the
* text file in reverse order.
* */
void reverse(int N) {
int i = 0;
for(i = N-1; i >= 0; i--) {
if(i == 0) {
printf("%s \n",wordList[i]);
} else if(strcmp(wordList[i],"") == 0) { /* improve this in main-> memory allocation */
continue;
}else {
printf("%s ",wordList[i]);
}
}
return;
}
/**
* Function first: Prints the first char of each
* word in the file.
* */
void first(int N) {
char firstChar;
int i = 0;
for(i = 0; i < N; i++) {
firstChar = *wordList[i];
printf("%c",firstChar);
}
printf("\n");
return;
}
/**
* Function middle: Prints the middle char of each word
* from the given file.
* */
void middle(int N) {
int middleIndex = 0;
int i = 0;
char midChar;
for(i = 0; i < N; i++) {
if((strlen(wordList[i]) % 2) == 0) { /* artios */
middleIndex = ((strlen(wordList[i]) / 2) - 1);
midChar = wordList[i][middleIndex];
}
else { /* peritos */
middleIndex = (int) ceil((strlen(wordList[i]) / 2));
midChar = wordList[i][middleIndex];
}
printf("%c",midChar);
}
printf("\n");
return;
}
/**
* Function last: Prints the last char of each
* word from the given file.
* */
void last(int N) {
int i = 0;
char lastChar;
int lastPos;
for(i = 0; i < N; i++) {
lastPos = strlen(wordList[i]) - 1;
lastChar = wordList[i][lastPos];
printf("%c",lastChar);
}
printf("\n");
return;
}
/**
* Function count: Prints the number of times
* that the selected word is found inside the N first words
* of the file.
* */
int count(int N, char *word, int callID) {
int i = 0;
int count = 0;
for(i = 0; i < N; i++) {
if(strcmp(word,wordList[i]) == 0) {
count++;
}
}
if(callID == 0) { /* if callID == 0 (main called count and we want the output) */
printf("%d",count);
printf("\n");
}
return count;
}
void sortedCount(int N) {
int i,j = 0;
int *occurrences;
int *wordCounts;
char **uniqueWords;
/* mem allocation */
uniqueWords = malloc_or_end(N * sizeof(char*)); /* worst case: every word is unique */
wordCounts = malloc_or_end(N * sizeof(int));
occurrences = malloc_or_end(N * sizeof(int));
/* initialize rootWord and occurrences for the "each word is unique and occurs only once" scenario */
for(i = 0; i < N; i++) {
uniqueWords[i] = malloc_or_end(MAX_STRING_SIZE * sizeof(char));
occurrences[i] = 1;
}
determineUniqueWords(occurrences,wordList,N);
/* populate the wordCounts & uniqueWords "arrays" with the appropriate data in order to sort them successfully */
for(i = 0; i < N; i++) {
if(occurrences[i] > 0) {
wordCounts[i] = count(N,wordList[i],1);
uniqueWords[i] = wordList[i];
}
}
for(i = 0; i < N; i++) {
free(uniqueWords[i]);
}
free(uniqueWords);
free(occurrences);
free(wordCounts);
return;
}
int main(int argc,char *argv[]) { /* argv[1] = op argv[2] = name argv[3] = <word> */
int N = -1;
int i = 0;
int spaceNum,nlNum = -1;
file = fopen(argv[2],"r");
if(file == (FILE *) NULL) { /* check if the file opened successfully */
fprintf(stderr,"Cannot open file\n");
}
fscanf(file,"%d",&N); /* get the N number */
wordList = malloc_or_end(N * sizeof(char *)); /* allocate memory for pointers */
for(i = 0; i < N; i++) {
wordList[i] = malloc_or_end(MAX_STRING_SIZE * sizeof(char)); /* allocate memory for strings */
}
populateWordsArray(N);
if(strcmp(argv[1],"-reverse") == 0) {
reverse(N);
} else if(strcmp(argv[1],"-first") == 0) {
first(N);
} else if(strcmp(argv[1],"-middle") == 0) {
middle(N);
} else if(strcmp(argv[1],"-last") == 0) {
last(N);
} else if((strcmp(argv[1],"-count") == 0) && argv[3] != NULL) {
i = count(N,argv[3],0);
} else if((strcmp(argv[1],"-sorted") == 0) && (strcmp(argv[3],"-count") == 0)) {
sortedCount(N);
} else {
/* i only wish i could print something here */
}
/* End of program operations */
for(i = 0; i < N; i++) {
free(wordList[i]);
}
free(wordList);
fclose(file);
return 0;
}
You are overwriting the value of a pointer to heap memory on line 185:
uniqueWords[i] = wordList[i];
This means that when you free it later, you are actually freeing the allocated rows in wordList. Now you have two problems:
When you free the wordList rows on lines 244-246, it will be a double-free
You are losing your reference to the uniqueWords rows.
Use strcpy to assign to a dynamically-allocated string rather than the = operation.
I have had a problem with finding the longest common word in two strings for some time now. First I had an idea of doing it with "isspace" function, but couldn't figure out how to find a common word. Then "strcmp" came to my mind, but so far I was only able to compare two strings. I was thinking of some way to incorporate strcmp and isspace in order to find the different words and then use a temp value to find the longest one, but I couldn't think of the correct code to do so.
#include <stdio.h>
int strcmp(char s[],char t[]);
void main()
{
char s[20],t[20];
printf("Type in a string s.\n");
gets(s);
printf("Type in a string t.\n");
gets( t );
printf("The result of comparison=%d\n",strcmp(s,t));
return 0;
}
int strcmp(char s[],char t[])
{
int i;
for(i=0;s[i]==t[i];i++)
if(s[i]=='\0')
return( 0 );
return(s[i]-t[i]);
}
Please help me with this one. All ideas (and code) are welcomed and appreciated. Thank you in advance!
Edit::
I have been battling with this one for a while and I think I have the solution, however it's a very rigid method. The program has a bug, probably with array "ptrArray1", but I cannot fix it.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int returnArrayOfWords (char* str4Parsing, char* arrayParsed[])
{
// returns the length of array
char seps[] = " \t\n"; // separators
char *token = NULL;
char *next_token = NULL;
int i = 0;
// Establish string and get the first token:
token = strtok( str4Parsing, seps);
// While there are tokens in "str4Parsing"
while ((token != NULL))
{
// Get next token:
arrayParsed[i] = token;
//printf( " %s\n", arrayParsed[i] );//to be commented
token = strtok( NULL, seps);
i++;
}
return i;
}
void printArr(char *arr[], int n)
{
int i;
for ( i = 0; i < n; i++)
{
printf("Element %d is %s \n", i, arr[i]);
}
}
void findLargestWord(char *ptrArray1[], int sizeArr1, char *ptrArray2[], int sizeArr2)
{
int maxLength = 0;
char *wordMaxLength = NULL ;
int i = 0, j = 0;
char *w1 = NULL, *w2 = NULL; /*pointers*/
int currLength1 = 0, currLength2 = 0 ;
//printArr(&ptrArray1[0], sizeArr1);
//printArr(&ptrArray2[0], sizeArr2);
for (i = 0; i < sizeArr1; i++)
{
// to find the largest word in the array
w1 = (ptrArray1[i]); // value of address (ptrArray1 + i)
currLength1 = strlen(w1);
//printf("The word from the first string is: %s and its length is : %d \n", w1, currLength1); // check point
for (j = 0; j < sizeArr2; j++)
{
w2 = (ptrArray2[j]); // value of address (ptrArray2 + j)
currLength2 = strlen(w2);
//printf("The word from the second string is : %s and its length is : %d \n", w2, currLength2); // check point
if (strcoll(w1, w2) == 0 && currLength1 == currLength2)
// compares the strings
{
if (currLength2 >= maxLength)
// in the variable maxLength -> the length of the longest word
{
maxLength = currLength2;
wordMaxLength = w2;
printf("The largest word for now is : %s and its length is : %d \n", wordMaxLength, maxLength); // check point
}
}
}
}
printf("The largest word is: %s \n", wordMaxLength);
printf("Its length is: %d \n", maxLength);
}
int main ()
{
int n = 80; /*max number of words in string*/
char arrS1[80], arrS2[80];
char *ptrArray1 = NULL, *ptrArray2 = NULL;
int sizeArr1 = 0, sizeArr2 = 0;
// to allocate memory:
ptrArray1 = (char*)calloc(80, sizeof(char));
if(ptrArray1 == NULL)
{
printf("Error! Memory for Pointer 1 is not allocated.");
exit(0);
}
ptrArray2 = (char*)calloc(80, sizeof(char));
if(ptrArray2 == NULL)
{
printf("Error! Memory for Pointer 2 is not allocated.");
exit(0);
}
printf("Type your first string: ");
fgets(arrS1, 80, stdin);
sizeArr1 = returnArrayOfWords (arrS1, &ptrArray1); // sizeArr1 = number of elements in array 1
printf("Type your second string: ");
fgets(arrS2, 80, stdin);
sizeArr2 = returnArrayOfWords (arrS2, &ptrArray2); // sizeArr2 = number of elements in array 2
findLargestWord(&ptrArray1, sizeArr1, &ptrArray2, sizeArr2);
free(ptrArray1);
free(ptrArray2);
return 0;
}
I also tried to use the latter two posted soltuions, but I have a problem with working with them as stated below.
Any help with my code, fixing my problems with the latter solutions or coming up with new solutions is welcomed. Thank you all in advance!
PS. I'm sorry if my code is poorly placed. I'm still not very good with using the placement.
There are a large number of ways to approach the problem. Below, a pointer to each character in one of the strings is used to search the other for matching characters using strchr. After matching characters are found, a comparison loop runs advancing each of the pointers to devise the length of the common substring present, if any.
The routine, match characters, check substring length, repeat, continues so longs as strchr returns a valid pointer.Each time a longer substring is found, the max length is updated for return and the substring present is copied to r with strncpy and nul-terminated so that the text of the longest common string is available to the calling function, main here.
This is a rather brute force method, and there may be a few additional tweaks to improve efficiency. The function itself is:
/** return length of longest common substring in 'a' and 'b'.
* by searching through each character in 'a' for each match
* in 'b' and comparing substrings present at each match. the
* size of the longest substring is returned, the test of the
* longest common substring is copied to 'r' and made available
* in the calling function. (the lengths should also be passed
* for validation, but that is left as an exercise)
*/
size_t maxspn (const char *a, const char *b, char *r)
{
if (!a||!b||!*a||!*b) return 0; /* valdate parameters */
char *ap = (char *)a; /* pointer to a */
size_t max = 0; /* max substring char */
for (; *ap; ap++) { /* for each char in a */
char *bp = (char *)b; /* find match in b with strchr */
for (; *bp && (bp = strchr (bp, *ap)); bp++) {
char *spa = ap, *spb = bp; /* search ptr initialization */
size_t len = 0; /* find substring len */
for (; *spa && *spb && *spa == *spb; spa++, spb++) len++;
if (len > max) { /* if max, copy to r */
strncpy (r, ap, (max = len));
r[max] = 0; /* nul-terminate r */
}
}
}
return max;
}
The length max is returned, and then updates to r during function execution cause r to hold the string associated with the longest substring match.
Additional improvements were to remove gets which was removed in C11 without deprecation due to its security risk. It should no longer be used by any sane coder (that should cover about 40% of us). Putting the remaining bits together, a small bit of test code could be:
#include <stdio.h>
#include <string.h>
#define MAXC 128
size_t maxspn (const char *a, const char *b, char *r);
void rmlf (char *s);
int main (void) {
char res[MAXC] = "", s[MAXC] = "", t[MAXC] = "";
printf ("Type in a string 's': ");
if (!fgets (s, MAXC, stdin)) { /* validate 's' */
fprintf (stderr, "error: invalid input for 's'.\n");
return 1;
}
rmlf (s); /* remove trailing newline */
printf ("Type in a string 't': ");
if (!fgets (t, MAXC, stdin)) { /* validate 't' */
fprintf (stderr, "error: invalid input for 's'.\n");
return 1;
}
rmlf (t); /* remove trailing newline */
/* obtain longest commons substring between 's' and 't' */
printf ("\nThe longest common string is : %zu ('%s')\n",
maxspn (s, t, res), res);
return 0;
}
/** return length of longest common substring in 'a' and 'b'.
* by searching through each character in 'a' for each match
* in 'b' and comparing substrings present at each match. the
* size of the longest substring is returned, the test of the
* longest common substring is copied to 'r' and made available
* in the calling function. (the lengths should also be passed
* for validation, but that is left as an exercise)
*/
size_t maxspn (const char *a, const char *b, char *r)
{
if (!a||!b||!*a||!*b) return 0; /* valdate parameters */
char *ap = (char *)a; /* pointer to a */
size_t max = 0; /* max substring char */
for (; *ap; ap++) { /* for each char in a */
char *bp = (char *)b; /* find match in b with strchr */
for (; *bp && (bp = strchr (bp, *ap)); bp++) {
char *spa = ap, *spb = bp;
size_t len = 0; /* find substring len */
for (; *spa && *spb && *spa == *spb; spa++, spb++) len++;
if (len > max) { /* if max, copy to r */
strncpy (r, ap, (max = len));
r[max] = 0; /* nul-terminate r */
}
}
}
return max;
}
/** remove trailing newline from 's'. */
void rmlf (char *s)
{
if (!s || !*s) return;
for (; *s && *s != '\n'; s++) {}
*s = 0;
}
Example Use/Output
$ ./bin/strspn
Type in a string 's': a string with colors123456789 all blue
Type in a string 't': a string without colors1234567890 all red
The longest common string is : 16 (' colors123456789')
or, another that may be easier to visualize:
$ ./bin/strspn
Type in a string 's': green eel
Type in a string 't': cold blue steel
The longest common string is : 3 ('eel')
Look over the code and compare with the other answers. Let me know if you have any additional questions. There are a few other validations that should be added to insure text is not written beyond the ends of buffers, etc.. Hopefully this will provide a bit of help or an alternative approach.
Additional Substrings
Just to make sure you and I are seeing the same thing, I have included additional examples of use below. There is no error, and the code preforms as intended. If you are having trouble modifying the code, please let me know what you are attempting to do and I can help. Each of the pointer increments in my code above are validated. If you change anything regarding the pointer increment or nul-termination, the code will not work unless you account for the changes in the validations as well.
$ ./bin/strspn
Type in a string 's': 1
Type in a string 't':
The longest common string is : 0 ('')
$ ./bin/strspn
Type in a string 's': A man a plan a canal panama
Type in a string 't': a man a plan a river panama
The longest common string is : 14 (' man a plan a ')
$ ./bin/strspn
Type in a string 's': this is my favorite string
Type in a string 't': this is my favoritist string
The longest common string is : 18 ('this is my favorit')
$ ./bin/strspn
Type in a string 's': not the same until here
Type in a string 't': cant be equal till here
The longest common string is : 6 ('l here')
$ ./bin/strspn
Type in a string 's': some str with ten in the middle
Type in a string 't': a string often ignorded
The longest common string is : 5 ('ten i')
Longest Common Word
OK, after I finally understand what you are trying to accomplish, you can select the longest common word between the two strings 's' and 't' by tokenizing each string with strtok, saving a pointer to each word in each string in separate pointer arrays, and then simply iterating over the pointer arrays to select the longest common word (1st if multiple common words of the same length). Something as simple as the following is all you need.
NOTE strtok modifies the strings 's' and 't', so make a copy if you need to preserve the originals.
/** return length of longest common word in 'a' and 'b'.
* by tokenizing each word in 'a' & 'b' and iterating over
* each, returning the length of the logest match, and updating
* 'r' to contain the longest common word.
*/
size_t maxspnwhole (char *a, char *b, char *r)
{
if (!a||!b||!*a||!*b) return 0; /* valdate parameters */
char *arra[MAXC] = {NULL}, *arrb[MAXC] = {NULL};
char *ap = a, *bp = b; /* pointers to a & b */
char *delim = " .,-;\t\n"; /* word delimiters */
size_t i, j, len, max, na, nb; /* len, max, n-words */
len = max = na = nb = 0;
/* tokenize both strings into pointer arrays */
for (ap = strtok (a, delim); ap; ap = strtok (NULL, delim))
arra[na++] = ap;
for (bp = strtok (b, delim); bp; bp = strtok (NULL, delim))
arrb[nb++] = bp;
for (i = 0; i < na; i++) /* select longest common word */
for (j = 0; j < nb; j++)
if (*arra[i] == *arrb[j]) /* 1st chars match */
if (!strcmp (arra[i], arrb[j])) { /* check word */
len = strlen (arra[i]);
if (len > max) { /* if longest */
max = len; /* update max */
strcpy (r, arra[i]); /* copy to r */
}
}
return max;
}
Integrating it with the other code, you can compare the results like this:
#include <stdio.h>
#include <string.h>
#define MAXC 128
size_t maxspn (const char *a, const char *b, char *r);
size_t maxspnwhole (char *a, char *b, char *r);
void rmlf (char *s);
int main (void) {
char res[MAXC] = "", s[MAXC] = "", t[MAXC] = "";
printf ("Type in a string 's': ");
if (!fgets (s, MAXC, stdin)) { /* validate 's' */
fprintf (stderr, "error: invalid input for 's'.\n");
return 1;
}
rmlf (s); /* remove trailing newline */
printf ("Type in a string 't': ");
if (!fgets (t, MAXC, stdin)) { /* validate 't' */
fprintf (stderr, "error: invalid input for 's'.\n");
return 1;
}
rmlf (t); /* remove trailing newline */
/* obtain longest commons substring between 's' and 't' */
printf ("\nThe longest common string is : %zu ('%s')\n",
maxspn (s, t, res), res);
/* obtain longest commons word between 's' and 't' */
printf ("\nThe longest common word is : %zu ('%s')\n",
maxspnwhole (s, t, res), res);
return 0;
}
/** return length of longest common word in 'a' and 'b'.
* by tokenizing each word in 'a' & 'b' and iterating over
* each, returning the length of the logest match, and updating
* 'r' to contain the longest common word.
*/
size_t maxspnwhole (char *a, char *b, char *r)
{
if (!a||!b||!*a||!*b) return 0; /* valdate parameters */
char *arra[MAXC] = {NULL}, *arrb[MAXC] = {NULL};
char *ap = a, *bp = b; /* pointers to a & b */
char *delim = " .,-;\t\n"; /* word delimiters */
size_t i, j, len, max, na, nb; /* len, max, n-words */
len = max = na = nb = 0;
/* tokenize both strings into pointer arrays */
for (ap = strtok (a, delim); ap; ap = strtok (NULL, delim))
arra[na++] = ap;
for (bp = strtok (b, delim); bp; bp = strtok (NULL, delim))
arrb[nb++] = bp;
for (i = 0; i < na; i++)
for (j = 0; j < nb; j++)
if (*arra[i] == *arrb[j])
if (!strcmp (arra[i], arrb[j])) {
len = strlen (arra[i]);
if (len > max) {
max = len;
strcpy (r, arra[i]);
}
}
return max;
}
/** return length of longest common substring in 'a' and 'b'.
* by searching through each character in 'a' for each match
* in 'b' and comparing substrings present at each match. the
* size of the longest substring is returned, the test of the
* longest common substring is copied to 'r' and made available
* in the calling function. (the lengths should also be passed
* for validation, but that is left as an exercise)
*/
size_t maxspn (const char *a, const char *b, char *r)
{
if (!a||!b||!*a||!*b) return 0; /* valdate parameters */
char *ap = (char *)a; /* pointer to a */
size_t max = 0; /* max substring char */
for (; *ap; ap++) { /* for each char in a */
char *bp = (char *)b; /* find match in b with strchr */
for (; *bp && (bp = strchr (bp, *ap)); bp++) {
char *spa = ap, *spb = bp;
size_t len = 0; /* find substring len */
for (; *spa && *spb && *spa == *spb; spa++, spb++) len++;
if (len > max) { /* if max, copy to r */
strncpy (r, ap, (max = len));
r[max] = 0; /* nul-terminate r */
}
}
}
return max;
}
/** remove trailing newline from 's'. */
void rmlf (char *s)
{
if (!s || !*s) return;
for (; *s && *s != '\n'; s++) {}
*s = 0;
}
Example Use/Output
$ ./bin/strlongestcmn
Type in a string 's': I have a huge boat.
Type in a string 't': I have a small boat.
The longest common string is : 9 ('I have a ')
The longest common word is : 4 ('have')
Look it over and let me know if you have any further questions.
This example prints the longest substring given two input strings.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int *lcommon(char *str1, char *str2) {
int strlen1 = (unsigned) strlen(str1);
int strlen2 = (unsigned) strlen(str2);
int i, j, k;
int longest = 0;
int **ptr = malloc(2 * sizeof(int *));
static int *ret;
ret = calloc((unsigned) strlen1 + 1, sizeof(int));
for (i = 0; i < 2; i++)
ptr[i] = calloc((unsigned) strlen2, sizeof(int));
k = 0;
for (i = 0; i < strlen1; i++) {
memcpy(ptr[0], ptr[1], strlen2 * sizeof(int));
for (j = 0; j < strlen2; j++) {
if (str1[i] == str2[j]) {
if (i == 0 || j == 0) {
ptr[1][j] = 1;
} else {
ptr[1][j] = ptr[0][j-1] + 1;
}
if (ptr[1][j] > longest) {
longest = ptr[1][j];
k = 0;
ret[k++] = longest;
}
if (ptr[1][j] == longest) {
ret[k++] = i;
ret[k] = -1;
}
} else {
ptr[1][j] = 0;
}
}
}
for (i = 0; i < 2; i++)
free(ptr[i]);
free(ptr);
ret[0] = longest;
return ret;
}
int main(int argc, char *argv[]) {
int i, longest, *ret;
if (argc != 3) {
printf("usage: longest-common-substring string1 string2\n");
exit(1);
}
ret = lcommon(argv[1], argv[2]);
if ((longest = ret[0]) == 0) {
printf("There is no common substring\n");
exit(2);
}
i = 0;
while (ret[++i] != -1) {
printf("%.*s\n", longest, &argv[1][ret[i]-longest+1]);
}
exit(0);
}
Test
$ ./a.out computerprogramming javaprogrammer
programm
You can read more about the problem here.
You can also use an interactive program where you write the strings in the console:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int *lcommon(char *str1, char *str2) {
int strlen1 = (unsigned) strlen(str1);
int strlen2 = (unsigned) strlen(str2);
int i, j, k;
int longest = 0;
int **ptr = malloc(2 * sizeof(int *));
static int *ret;
ret = calloc((unsigned) strlen1 + 1, sizeof(int));
for (i = 0; i < 2; i++)
ptr[i] = calloc((unsigned) strlen2, sizeof(int));
k = 0;
for (i = 0; i < strlen1; i++) {
memcpy(ptr[0], ptr[1], strlen2 * sizeof(int));
for (j = 0; j < strlen2; j++) {
if (str1[i] == str2[j]) {
if (i == 0 || j == 0) {
ptr[1][j] = 1;
} else {
ptr[1][j] = ptr[0][j - 1] + 1;
}
if (ptr[1][j] > longest) {
longest = ptr[1][j];
k = 0;
ret[k++] = longest;
}
if (ptr[1][j] == longest) {
ret[k++] = i;
ret[k] = -1;
}
} else {
ptr[1][j] = 0;
}
}
}
for (i = 0; i < 2; i++)
free(ptr[i]);
free(ptr);
ret[0] = longest;
return ret;
}
int main(int argc, char *argv[]) {
int i, longest, *ret;
if (argc != 3) {
//printf("usage: longest-common-substring string1 string2\n");
char s[20], t[20];
printf("Type in a string s.\n");
fgets(s, 20, stdin);
printf("Type in a string t.\n");
fgets(t, 20, stdin);
ret = lcommon(s, t);
if ((longest = ret[0]) == 0) {
printf("There is no common substring\n");
exit(2);
}
i = 0;
while (ret[++i] != -1) {
printf("%.*s\n", longest, &s[ret[i] - longest + 1]);
}
//printf("The result of comparison=%d\n", strcmp(s, t));
exit(0);
} else { }
ret = lcommon(argv[1], argv[2]);
if ((longest = ret[0]) == 0) {
printf("There is no common substring\n");
exit(2);
}
i = 0;
while (ret[++i] != -1) {
printf("%.*s\n", longest, &argv[1][ret[i] - longest + 1]);
}
exit(0);
}
Test
Type in a string s.
string1
Type in a string t.
string2
string
I have a program that uses word search. I have a data file which contains the puzzle and the words. What can i implement into my program so that it reads the file and stores the letters present in it as an array?
Example of the data file (it is called testdata):
h e l l o a c d
f g b w o r l d
h a c c v b n a
e q b x n t q q
y e h n c a q r
hello
world
hey
I want to store all the letters in a 2-d array.
Also, I need to store all the words in a 1-dimensional array.
The maximum number of rows of columns or rows that AxA square of letters that is possible in a data file is 25. So, I believe that I should declare an array of that size for the letter and then write them into that array.
I just can't figure out how to read them into that array. There is a space after each letter in the array and no spaces in the words so I think that might be helpful when putting the letters in one array and words in another.
Given your question, and your input, there are a few questions, but in the interest of time, for now, I have made some assumptions about the dimensions of the array, i.e. that it is not necessarily square (as implied by columns or rows that AxA square). The actual data sample disagrees, so I wrote a routine that counts everything as it goes. The letter array is simply an array of arrays, but since it is stored in sequential memory, it just looks like one long array. The strings are each in there own location as well. In any case, this code should illustrate enough to get you on the right track...
#include <ansi_c.h>
#include <stdio.h>
void GetFileContents(char *file, int *nWords, int *lw, int *r, int *c);
void allocMemoryStr(int numStrings, int max);
void allocMemoryLtr(int numStrings, int max);
void freeMemoryStr(int numStrings);
void freeMemoryLtr(int numletters);
#define FILENAME "c:\\dev\\play\\_puzzle.txt"
char **letters;
char **strings;
int main()
{
int longest, cnt, wCount, rows, cols, i;
char line[260];
FILE *fp;
char *buf=0;
GetFileContents(FILENAME, &wCount, &longest, &rows, &cols);
allocMemoryStr(wCount, longest); //for strings
allocMemoryLtr(rows*cols, 1); //for strings
//read file into string arrays
fp = fopen(FILENAME, "r");
cnt=0;
for(i=0;i<rows;i++)
{
fgets(line, 260, fp);
buf = strtok(line, " \n");
while(buf)
{
strcpy(letters[cnt], buf);
buf = strtok(NULL, " \n");
cnt++; //use as accurate count of words.
}
}
cnt=0;
while(fgets(line, 260, fp)) //get remainder of lines into strings
{
//[EDIT]removed fgets()
buf = strtok(line, " \n");
while(buf)
{
strcpy(strings[cnt], buf);
buf = strtok(NULL, " \n");
cnt++; //use as accurate count of words.
}
}
fclose(fp);
freeMemoryStr(wCount);
freeMemoryLtr(rows*cols);
return 0;
}
void GetFileContents(char *file, int *nWords, int *lw, int *r, int *c)
{
char line[260];
FILE *fp;
char *buf=0;
char temp[80];
int wc=0, rc=0, cc=0, ck=0;
fp = fopen(FILENAME, "r");
while(fgets(line, 260, fp))
{
rc++;
buf = strtok(line, " \n");
while(buf)
{
strcpy(temp, buf); // word handler
if(strlen(temp) > 1)
{
wc++;
rc--; //
}
else if(strlen(temp) == 1) //leter handler
{
cc++;
(cc>ck)?(ck=cc):(cc=cc);
}
buf = strtok(NULL, " \n");
}
cc = 0;
}
fclose(fp);
*nWords = wc;
*r = rc;
*c = ck;
}
void allocMemoryStr(int numStrings, int max)
{
int i;
strings = calloc(sizeof(char*)*(numStrings+1), sizeof(char*));
for(i=0;i<numStrings; i++)
{
strings[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void allocMemoryLtr(int numletters, int max)
{
int i;
letters = calloc(sizeof(char*)*(numletters+1), sizeof(char*));
for(i=0;i<numletters; i++)
{
letters[i] = calloc(sizeof(char)*max + 1, sizeof(char));
}
}
void freeMemoryStr(int numStrings)
{
int i;
for(i=0;i<numStrings; i++)
if(strings[i]) free(strings[i]);
free(strings);
}
void freeMemoryLtr(int numletters)
{
int i;
for(i=0;i<numletters; i++)
if(letters[i]) free(letters[i]);
free(letters);
}
I would parse the file line by line and char by char looking for what i need. In the example (which is untested), i hold three counters to help filling the arrays correctly.
char letters[25][25];
char words[10][25]
int letters_x_pos = 0; // Row counter
int letters_y_pos = 0; // Column counter
int words_pos = 0;
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
letters[i][j] = '\0';
}
}
const char *line;
while (line = some_read_function()) {
if (!(strlen(line) > 1)) {
continue;
}
if (line[1] == ' ') {
// Line contains letters
const char *letter = line;
while (*letter != '\0') {
if (*letter == ' ' || *letter == '\n' || *letter == '\r') {
continue;
}
else {
letters[letters_x_pos][letters_y_pos++] = *letter;
}
if (letters_y_pos == 25) {
// Maximum reached
break;
}
letter++;
}
// Increment row counter and reset column counter
letters_x_pos++;
letters_y_pos = 0;
if (letters_x_pos == 25) {
// Maximum reached
break;
}
}
else {
// Line contains word
strncpy(words[words_pos++], line, 25);
if (words_pos == 25) {
// Maximum reached
break;
}
}
}