i have a problem with strcat into 2d array in c - c

im trying to add words from a file into a 2d array , the problem is that after the 7th word , the words start to shape up wierdly , does anyone knows that might cause this?
void count_words(WordCount **wclist, FILE *infile)
{
int num_words = 0;
char ch;
int k=0;
char **pook;
int flagA=0;
pook = malloc(4096*sizeof(char*));//creates a 2d array for every word from the file
for(int i = 0 ; i <4096 ; i++)
{
pook[i] = malloc(50 * sizeof(char*));
}
while((ch=fgetc(infile))!=EOF)
{
ch=tolower(ch);
if(flagA==0)
{
if(isalpha(ch)!=0)
{
num_words++;
flagA=1;
strcat(pook[k]+0, &ch);
}
}
else
{
if(isalpha(ch)!=0)
{
strcat(pook[k]+0, &ch);
}
else
{
flagA = 0;
k++;
}
}
}
for(int i =0 ; i < num_words ;i++)
{
printf("%s\n",pook[i]);
add_word(wclist , pook[i]);
}
}
the input :
input is text file that contains :
ilona.txt
main.c
makefile
wc_sort.o
word_count.c
word_count.h
words
this is how the output should look like :
ilona
txt
main
c
makefile
wc
sort
o
word
count
c
word
count
h
words
this is how the output realy is :
the output is :
ilona
txt
main
c
makefile
wc
sort
o
w o r d
c
o
u
n
t
c
w
o
r
d
t
h
words
*/

So your code has several mistakes, and I'm sure that as you will gain experience, your code will be less messy.
Previous comments have pointed to some of the bugs, so I'll just list them by order:
When you use 'malloc', it doesn't reset the values in the memory cells, so either you do it yourself, or as I suggest, just use 'calloc' - it's a better habit IMO.
You should check that the pointer you allocated memory to, hasn't received a NULL value (because the allocation has failed).
You first allocate memory for the pointers to the character arrays (AKA the number of strings), then allocate memory for the number of characters for each string). Notice that you have a mistake: pook[i] = malloc(50 * sizeof(char*));. This should be pook[i] = malloc(50 * sizeof(char)); because the second allocation is for characters and not pointers to characters.
Though the statement strcat(pook[k]+0, &ch); should be okay since &ch is a pointer to a character like declaring an array and using the pointer to the array, though note yourself that most compilers probably won't let you do that; In fact, I tried compiling your code in VS 2019, and it didn't even let me build it because strcat is an un-safe function.
In pook[k]+0, the zero is irrelevant, and can cause problems to the compilation as it sometimes might identify it as an arithmetic argument, instead of a pointer.
Also, IMO you should add comments before uploading your code, so It would be easier for other people to understand your code and help you find the solution faster, saving you time. Also, it's a must-do habit because you almost always will show other people your code, and even help you if you get lost in your own code.
My version of what you tried to do is the following:
void count_words (FILE* infile)
{
char** pook;
pook = calloc(4096,sizeof(char*)); // Allocate the number of strings
for (int i = 0; i < 4096; i++)
{
pook[i] = calloc(50, sizeof(char)); // Allocate the number of characters for each string
}
int num_words = 0, letter = 0; // Initialize a counter for the amount of words and letters
int flag = 0; // Initialize a flag that represents if we are currently in a word or not.
char ch; // A temp character
while ((ch = fgetc(infile)) != EOF)
{
if (isalpha(ch) == 0 && flag == 0) // If the current character isn't a letter, and we didn't start to read a word:
continue;
else if (isalpha(ch) == 0 && flag == 1) // If the current character isn't a letter, and we finished to read a word:
{
++num_words; // Because we finished reading a new word
flag = 0; // Because we're not in a word anymore
letter = 0; // Same as above
}
else if (isalpha(ch) != 0 && flag == 0) // If this is the first letter in the current word:
{
pook[num_words][letter] = ch; // Insert the value of the current character
++letter; // Advance to the next letter of pook[num_words]
flag = 1; // Because we're currently in a word
}
else if (isalpha(ch) != 0 && flag == 1) // If this is not the first letter of the current word:
{
pook[num_words][letter] = ch; // Insert the value of the current character
++letter; // Advance to the next letter of pook[num_words]
}
}
if (flag == 1) // If the last character before the EOF is still part of the word, we need to increment the num of words.
++num_words;
for (int i = 0; i < num_words; i++)
{
printf("%s\n", pook[i]);
}
}
The output is the following:
ilona
txt
main
c
makefile
wc
sort
o
word
count
c
word
count
h
words
Hope I helped,
Good luck!

Related

No output given when executing a program on C

When I compile this program with gcc:
#include <stdio.h>
/* This program accepts some text as an input and gives the output
* of longest word and shortest word lengths*/
int main(){
int c, i, wordcount, symbolcount, longestword, shortestword;
wordcount = symbolcount = longestword = shortestword = 0;
int wlength[1];
while((c = getchar()) != EOF){
++symbolcount;
if(c == ' ' || c == '\n' || c == '\t'){
++wordcount;
wlength[wordcount];
wlength[wordcount - 1] = symbolcount;
symbolcount = 0;
}
}
for(i = 0;i <= wordcount;)
wlength[0] = longestword;
wlength[i] = shortestword;
while(shortestword < 1){
if(shortestword == longestword){
continue;
++i;
}else if(shortestword < longestword && shortestword > 0){
shortestword = wlength[i];
break;
}
}
for(i = 0; i <= wordcount - 1; ++i){
if(wlength[i] > longestword){
longestword = wlength[i];
}else if(wlength[i] < longestword && wlength[i] > shortestword){
continue;
}else{
wlength[i] = shortestword;
}
}
printf("%d\t%d", longestword, shortestword);
return 0;
}
There are no errors or warnings. But when I try to run it, it accepts the input, but there is no output at all. Even when I press Ctrl + D(I work on a debian based distro), current terminal session is not suspended and the program just keeps running. What can be the problem?
The problem is that
int wlength[1];
only declares an array with one element, but you access out of bounds with
shortestword = wlength[i];
This is undefined behavior in C parlance, anything can happen, including what you observe.
To fix this, declare the array with as many elements as you expect i to be.
Make sure your loops over i only take values that do not exceed the array element count.
You have declared an integer array wlength whose size is 2 i.e.,
int wlength[1];
and within the if condition you increment wordcount.
Now assume you have 4 words in a line and wordcount keep on increasing and will be assigned to wlength index but as you have defined the array size 2 , where it overflows. Thus when that is used further in
shortestword = wlength[i];
and
longestword = wlength[i];
it causes junk values to be assigned.
There are several things wrong with your program.
You don't allocate space for only one word.
You have an infinite loop over i. That's acutally why you don't see any output: The program is stuck in this loop.
The second while loop doesn't look as if you knew what you were doing there. I doubt that the condition shortestword < 1 will ever be true. A contunue before other statements makes those statements useless. And what exactly is i here. (Okay, perhaps the while is supposed to be inside the for loop? If so, you need curly braces on the loop body.)
Most of the errors stem from a misunderstanding of the problem. You do not need to store the lengths of all words in order to find the sortest and longest words. Just keeping track of the length of the current word is enough. The algorithm goes like this:
set longest to 0.
set shortest to a large number.
set length to 0.
for each character in the input:
if it is a white-space character:
update longest and shortest if necessary.
reset length to 0.
otherwise:
increase length
This lets you find the longest word in Moby-Dick without having to store more than the current word length. in C, it may look like this:
#include <stdio.h>
int main(void)
{
int longest = 0; // length of currently longest word
int shortest = 0; // length of currently shortest word
int length = 0; // length of current word
int c = getchar();
while (c != EOF) {
if (c == ' ' || c == '\n' || c == '\t') {
if (length) {
if (longest == 0 || length < shortest) shortest = length;
if (length > longest) longest = length;
length = 0;
}
} else {
length++;
}
c = getchar();
}
printf("max: %d\nmin: %d\n", longest, shortest);
return 0;
}

Take a vocabulary from a txt file in c

i have an assignment where i have to extract the respective morse code per word that is stored in a text file. it's a word and it's code per line so i save the whole line and try to separate the string as: Y = ..-.
char letter;
char meaning[30][30];
char letters[30];
while(i<30){
int i =0;
gets(code,10,fPointer);
//i save the position 0 in the char array because the letter is always is the 1st position
letter = code[0];
letters[i]= letter;
i++;
int S = strlen(code);
for(int f=3;f<=N;f++){
//i need to save the rest of the line in a different array but i don't know how}
i'm new to programming so i don't really know too many functions so ideas on how to solve them or new functions will be welcome
basically i know how to take the whole line and put it in an array, the first position i save it as it is the letter and the rest i don't know how to separate it
i want 2 arrays
array1[30] = "a" ,"b", "c",
array2[30][30] = ".--", "..-",
so a and it's code are both in the position 0 and all the other letters
You can simply use strcpy() to copy the rest of the line to meanings[i].
You shouldn't initialize i = 0; inside the loop, since then it will never reach 30. And you shouldn't increment i until after you copy to meanings[i]. You can use a for loop instead of while to simplify all of this.
for (i = 0; i < 30; i++){
char *res = fgets(code,10,fPointer);
if (!res) { // EOF or error
break;
}
//i save the position 0 in the char array because the letter is always is the 1st position
letter = code[0];
letters[i]= letter;
int S = strlen(code);
if (code[S-1] == '\n') { // trim newline
code[S-1] = '\0';
}
strcpy(meaning[i], &code[4]);
}
You can also use sscanf() to extract parts of the line.
for (i = 0; i < 30; i++){
char *res = fgets(code,10,fPointer);
if (!res) { // EOF or error
break;
}
if (sscanf(code, "%c = %s", &letters[i], meaning[i]) != 2) {
break; // line isn't formatted correctly
}
}

Manipulating dynamically allocated 2D char arrays in C

I'm having trouble with trying to manipulate 2d dynamic arrays in C. What I want to do is to store a char string in every row of the the 2d array then perform a check to see if the string contains a certain character, if so remove all occurrences then shift over the empty positions. What's actually happening is I get an exit status 1.
More about the problem, for example if I have
Enter string 1: testing
Enter string 2: apple
Enter string 3: banana
I would want the output to become
What letter? a // ask what character to search for and remove all occurences
testing
pple
bnn
Here is my full code:
#include <stdio.h>
#include <stdlib.h>
void removeOccurences2(char** letters, int strs, int size, char letter){
// Get size of array
// Shift amount says how many of the letter that we have removed so far.
int shiftAmt = 0;
// Shift array says how much we should shift each element at the end
int shiftArray[strs][size];
// The first loop to remove letters and put things the shift amount in the array
int i,j;
for(i=0;i < strs; i++){
for(j = 0; j < size - 1; j++) {
if (letters[i][j] == '\0'){
break;
}
else {
// If the letter matches
if(letter == letters[i][j]){
// Set to null terminator
letters[i][j] = '\0';
// Increase Shift amount
shiftAmt++;
// Set shift amount for this position to be 0
shiftArray[i][j] = 0;
}else{
// Set the shift amount for this letter to be equal to the current shift amount
shiftArray[i][j] = shiftAmt;
}
}
}
}
// Loop back through and shift each index the required amount
for(i = 0; i < strs; i++){
for(j = 0; j < size - 1; j++) {
// If the shift amount for this index is 0 don't do anything
if(shiftArray[i][j] == 0) continue;
// Otherwise swap
letters[i][j - shiftArray[i][j]] = letters[i][j];
letters[i][j] = '\0';
}
//now print the new string
printf("%s", letters[i]);
}
return;
}
int main() {
int strs;
char** array2;
int size;
int cnt;
int c;
char letter;
printf("How many strings do you want to enter?\n");
scanf("%d", &strs);
printf("What is the max size of the strings?\n");
scanf("%d", &size);
array2 = malloc(sizeof(char*)*strs);
cnt = 0;
while (cnt < strs) {
c = 0;
printf("Enter string %d:\n", cnt + 1);
array2[cnt] = malloc(sizeof(char)*size);
scanf("%s", array2[cnt]);
cnt += 1;
}
printf("What letter?\n");
scanf(" %c", &letter);
removeOccurences2(array2,strs,size,letter);
}
Thanks in advance!
You can remove letters from a string in place, because you can only shorten the string.
The code could simply be:
void removeOccurences2(char** letters, int strs, int size, char letter){
int i,j,k;
// loop over the array of strings
for(i=0;i < strs; i++){
// loop per string
for(j = 0, k=0; j < size; j++) {
// stop on the first null character
if (letters[i][j] == '\0'){
letters[i][k] = 0;
break;
}
// If the letter does not match, keep the letter
if(letter != letters[i][j]){
letters[i][k++] = letters[i][j];
}
}
//now print the new string
printf("%s\n", letters[i]);
}
return;
}
But you should free all the allocated arrays before returning to environment, and explicitely return 0 at the end of main.
Well, there are several issues on your program, basically you are getting segmentation fault error because you are accessing invalid memory which isn't allocated by your program. Here are some issues I found:
shiftAmt isn't reset after processing/checking each string which lead to incorrect value of shiftArray.
Values of shiftArray only set as expected for length of string but after that (values from from length of each string to size) are random numbers.
The logic to delete occurrence character is incorrect - you need to shift the whole string after the occurrence character to the left not just manipulating a single character like what you are doing.
1 & 2 cause the segmentation fault error (crash the program) because it causes this line letters[i][j - shiftArray[i][j]] = letters[i][j]; access to unexpected memory. You can take a look at my edited version of your removeOccurences2 method for reference:
int removeOccurences2(char* string, char letter) {
if(!string) return -1;
int i = 0;
while (*(string+i) != '\0') {
if (*(string+i) == letter) {
memmove(string + i, string + i + 1, strlen(string + i + 1));
string[strlen(string) - 1] = '\0'; // delete last character
}
i++;
}
return 0;
}
It's just an example and there is still some flaw in its logics waiting for you to complete. Hint: try the case: "bananaaaa123"
Happy coding!
"...if the string contains a certain character, if so remove all occurrences then shift over the empty positions."
The original string can be edited in place by incrementing two pointers initially containing the same content. The following illustrates.:
void remove_all_chars(char* str, char c)
{
char *pr = str://pointer read
char *pw = str;//pointer write
while(*pr)
{
*pw = *pr++;
pw += (*pw != c);//increment pw only if current position == c
}
*pw = '\0';//terminate to mark last position of modified string
}
This is the cleanest, simplest form I have seen for doing this task. Credit goes to this answer.

C Program to Get Characters into Array and Reverse Order

I'm trying to create a C program that accepts a line of characters from the console, stores them in an array, reverses the order in the array, and displays the reversed string. I'm not allowed to use any library functions other than getchar() and printf(). My attempt is below. When I run the program and enter some text and press Enter, nothing happens. Can someone point out the fault?
#include <stdio.h>
#define MAX_SIZE 100
main()
{
char c; // the current character
char my_strg[MAX_SIZE]; // character array
int i; // the current index of the character array
// Initialize my_strg to null zeros
for (i = 0; i < MAX_SIZE; i++)
{
my_strg[i] = '\0';
}
/* Place the characters of the input line into the array */
i = 0;
printf("\nEnter some text followed by Enter: ");
while ( ((c = getchar()) != '\n') && (i < MAX_SIZE) )
{
my_strg[i] = c;
i++;
}
/* Detect the end of the string */
int end_of_string = 0;
i = 0;
while (my_strg[i] != '\0')
{
end_of_string++;
}
/* Reverse the string */
int temp;
int start = 0;
int end = (end_of_string - 1);
while (start < end)
{
temp = my_strg[start];
my_strg[start] = my_strg[end];
my_strg[end] = temp;
start++;
end--;
}
printf("%s\n", my_strg);
}
It seems like in this while loop:
while (my_strg[i] != '\0')
{
end_of_string++;
}
you should increment i, otherwise if my_strg[0] is not equal to '\0', that's an infinite loop.
I'd suggest putting a breakpoint and look what your code is doing.
I think you should look at your second while loop and ask yourself where my_string[i] is being incremented because to me it looks like it is always at zero...

Program runs too slowly with large input - C

The goal for this program is for it to count the number of instances that two consecutive letters are identical and print this number for every test case. The input can be up to 1,000,000 characters long (thus the size of the char array to hold the input). The website which has the coding challenge on it, however, states that the program times out at a 2s run-time. My question is, how can this program be optimized to process the data faster? Does the issue stem from the large char array?
Also: I get a compiler warning "assignment makes integer from pointer without a cast" for the line str[1000000] = "" What does this mean and how should it be handled instead?
Input:
number of test cases
strings of capital A's and B's
Output:
Number of duplicate letters next to each other for each test case, each on a new line.
Code:
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
int main() {
int n, c, a, results[10] = {};
char str[1000000];
scanf("%d", &n);
for (c = 0; c < n; c++) {
str[1000000] = "";
scanf("%s", str);
for (a = 0; a < (strlen(str)-1); a++) {
if (str[a] == str[a+1]) { results[c] += 1; }
}
}
for (c = 0; c < n; c++) {
printf("%d\n", results[c]);
}
return 0;
}
You don't need the line
str[1000000] = "";
scanf() adds a null terminator when it parses the input and writes it to str. This line is also writing beyond the end of the array, since the last element of the array is str[999999].
The reason you're getting the warning is because the type of str[10000000] is char, but the type of a string literal is char*.
To speed up the program, take the call to strlen() out of the loop.
size_t len = strlen(str)-1;
for (a = 0; a < len; a++) {
...
}
str[1000000] = "";
This does not do what you think it does and you're overflowing the buffer which results in undefined behaviour. An indexer's range is from 0 - sizeof(str) EXCLUSIVE. So you either add one to the
1000000 when initializing or use 999999 to access it instead. To get rid of the compiler warning and produce cleaner code use:
str[1000000] = '\0';
Or
str[999999] = '\0';
Depending on what you did to fix it.
As to optimizing, you should look at the assembly and go from there.
count the number of instances that two consecutive letters are identical and print this number for every test case
For efficiency, code needs a new approach as suggeted by #john bollinger & #molbdnilo
void ReportPairs(const char *str, size_t n) {
int previous = EOF;
unsigned long repeat = 0;
for (size_t i=0; i<n; i++) {
int ch = (unsigned char) str[i];
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
}
char *testcase1 = "test1122a33";
ReportPairs(testcase1, strlen(testcase1));
or directly from input and "each test case, each on a new line."
int ReportPairs2(FILE *inf) {
int previous = EOF;
unsigned long repeat = 0;
int ch;
for ((ch = fgetc(inf)) != '\n') {
if (ch == EOF) return ch;
if (isalpha(ch) && ch == previous) {
repeat++;
}
previous = ch;
}
printf("Pair count %lu\n", repeat);
return ch;
}
while (ReportPairs2(stdin) != EOF);
Unclear how OP wants to count "AAAA" as 2 or 3. This code counts it as 3.
One way to dramatically improve the run-time for your code is to limit the number of times you read from stdin. (basically process input in bigger chunks). You can do this a number of way, but probably one of the most efficient would be with fread. Even reading in 8-byte chunks can provide a big improvement over reading a character at a time. One example of such an implementation considering capital letters [A-Z] only would be:
#include <stdio.h>
#define RSIZE 8
int main (void) {
char qword[RSIZE] = {0};
char last = 0;
size_t i = 0;
size_t nchr = 0;
size_t dcount = 0;
/* read up to 8-bytes at a time */
while ((nchr = fread (qword, sizeof *qword, RSIZE, stdin)))
{ /* compare each byte to byte before */
for (i = 1; i < nchr && qword[i] && qword[i] != '\n'; i++)
{ /* if not [A-Z] continue, else compare */
if (qword[i-1] < 'A' || qword[i-1] > 'Z') continue;
if (i == 1 && last == qword[i-1]) dcount++;
if (qword[i-1] == qword[i]) dcount++;
}
last = qword[i-1]; /* save last for comparison w/next */
}
printf ("\n sequential duplicated characters [A-Z] : %zu\n\n",
dcount);
return 0;
}
Output/Time with 868789 chars
$ time ./bin/find_dup_digits <dat/d434839c-d-input-d4340a6.txt
sequential duplicated characters [A-Z] : 434893
real 0m0.024s
user 0m0.017s
sys 0m0.005s
Note: the string was actually a string of '0's and '1's run with a modified test of if (qword[i-1] < '0' || qword[i-1] > '9') continue; rather than the test for [A-Z]...continue, but your results with 'A's and 'B's should be virtually identical. 1000000 would still be significantly under .1 seconds. You can play with the RSIZE value to see if there is any benefit to reading a larger (suggested 'power of 2') size of characters. (note: this counts AAAA as 3) Hope this helps.

Resources