C character array comparison - c

Ok so I was doing a coding challenge on codewars.com and the challenge was to take a string as input and return a string where in place of the letters are instead the number of the alphabet that matches the letter.
Everything except letters are to be ignored.
ex: "aab" would return "1 1 2"
There should be a space between each number that represents a letter in the alphabet.
So, when I run this code on my IDE (which is xcode using c99) Everything looks good and the strcmp() function says the 2 strings are equal.
The website I'm on uses C11 I believe but I don't see that causing the error.
When I run this code on the challenge website it passes a couple of tests but then fails a couple also. It fails when the input string is "", and it also fails on the string that I have used in the code below, but again it does not fail when I run it on my ide.
My questions are:
1) Any idea what is causing this bug?
2) What would you have done differently as far as the code is concerned
Thanks
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char *alphabet_position(char *text)
{
int i,letter_position;
unsigned long int size = strlen(text);
char *result = malloc(sizeof(int)*size + 1);
char int_string[10];
char temp = ' ';
//If String wasn't blank
if (strcmp(text, "")!=0)
{
for (i=0; i<size-1; i++)
{
//If it is a letter
if (isalpha(text[i]))
{
temp = tolower(text[i]);
if (temp == 'a')
strcat(result, "1");
else
{
letter_position = temp - 'a' + 1;
sprintf(int_string,"%d",letter_position);
strcat(result, int_string);
}
//Print space after letter until the last letter
if (i!=size-2)
strcat(result, " ");
}
}
strcat(result, "\0");
return result;
}
else
{
strcat(result, "\0");
return result;
}
}
int main(void)
{
char *string = alphabet_position("The narwhal bacons at midnight.");
char *expected_output = "20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20";
printf("Your output %s\n", alphabet_position("The narwhal bacons at midnight."));
printf("Expt output %s\n", "20 8 5 14 1 18 23 8 1 12 2 1 3 15 14 19 1 20 13 9 4 14 9 7 8 20");
printf("\n");
printf("your len %lu\n", strlen(alphabet_position("The narwhal bacons at midnight.")));
printf("Expt len %lu\n", strlen(expected_output));
if (strcmp(string, expected_output)==0)
printf("Equal\n");
else
printf("Not equal\n");
return 0;
}

You have two serious problems.
First, you're not initializing the contents of the result array to an empty string. After you call malloc(), add:
result[0] = '\0';
malloc() doesn't initialize the memory it allocates. There's another function calloc() that takes slightly different arguments and initializes the memory to zeroes. But you only need the first character to be zero, so there's no need for that.
Second, the for loop is not processing the last character of text. It should be:
for (i = 0; i < size; i++)
Similarly, the test for whether to add a space should be if (i != size-1). Did you think strlen() counts the null character at the end?
The amount of space you specify in malloc() is not correct, but in practice it won't cause a problem. sizeof(int) has nothing to do with the number of characters it takes to show the value of an integer. Since you're just printing the alphabetical position, it will be at most 26, so you need 3 characters for every input character. Therefore, it should be:
char *result = malloc(3 * size + 1);
Your allocation works because sizeof(int) is generally at least 4, so you're allocating more than enough space.
There are other minor problems that don't affect the correctness of the result:
You don't need the if statement that treats 'a' specially, the code you have in else will work for all letters.
You don't need strcat(result, "\0") at the end. result has to already be null-terminated in order for you to use it as an argument to strcat(), so there's no point in using strcat() to add a null terminator.

Related

Reading the first line of a txt and converting it to int, but returning another value [C]

I've been trying to read a txt which has a specific structure:
The first line indicates the n-1 lines the whole txt file has.
All the other lines have the "structure" of a card (it's number and it's pattern).
ex: I have a txt which stores 13 cards, so the file in itself has 14 lines:
13
A T
2 P
3 D
13 P
2 P
4 C
8 D
11 T
8 C
9 C
10 T
9 T
7 P
(Note: T stands for clubs, D for diamonds, C for hearts and P for spades, it's in spanish).
I've tried extracting the first line to then create a dynamic array with the number given so that I can store each line in that array, but I fail to get the first value.
My code is:
#include <stdio.h>
#include <stdlib.h>
int leerLinea(){
char contenido[1];
FILE* pArchivo;
pArchivo = fopen("Mazo.txt","r");
if (pArchivo == NULL){
printf("No hay nada aqui!\n");
return 0;
}
else{
fgets(contenido,3,pArchivo);
printf("%s\n", contenido);
}
fclose(pArchivo);
return contenido[0];
}
int main(){
int a;
a = leerLinea();
printf("a's value is: %d\n",a);
return 0;
}
But when I run it I get:
13
a's value is: 49
Why is it returning other value, when it should be returning 13?
With fgets(contenido,3,pArchivo), you read in a string into a buffer that is to small for capturing at least 2 digits and the string termination character; For that statement, it should be at least char contenido[3].
The main issue is, however, that you mix strings with "pure" integral values, i.e. you read in a string but expect it to be converted correctly to a number simply by accessing the first digit of that string; Note that if contenido containded "13", contenido[0] would give character value '1', which in ASCII is 49.
To overcome this, read in the value as a number, i.e. using "%d"-format:
int leerLinea(){
int contenido = 0;
FILE* pArchivo;
pArchivo = fopen("Mazo.txt","r");
if (pArchivo == NULL){
printf("No hay nada aqui!\n");
return 0;
}
else{
fscanf(pArchivo,"%d",&contenido);
printf("%d\n", contenido);
}
fclose(pArchivo);
return contenido;
}
1 - Read a line of text with sufficient space for each character in the line, the line-feed and a null character. The below is 4 characters, so a buffer of 6 is needed.
13 P
Further, there is little gained by being so stingy with line buffers. Suggest 2x the maximize anticipated size to allow for some growth, leading/trailing whitespace.
#define LINE_MAX (4 + 1 + 1)
char contenido[LINE_MAX * 2];
2 - When reading a line, do not hard code in the 3, use sizeof() for consistent, easier to maintain code.
// fgets(contenido,3,pArchivo);
fgets(contenido, sizeof contenido, pArchivo);
3 - Rather than return the first character of a string (the code for the character '1' is 49), convert the string into an int/long with strtol() or atol(), etc. #Nicolas Guerin
// return contenido[0];
return atoi(contenido);
// or
return strtol(contenido, NULL, 10); // better
return atoi(contenido[0]);
The probleme you've got is that you return a char in an int function, every char as an integer value. That's true but not the decimal value of the char, the ascii value.
Atoi convert a char into an int.
ps: the way you do, it will only return 1 in the exemple

Word count program - stdin

For below question,
Write a program to read English text to end-of-data (type control-D to indicate end of data at a terminal, see below for detecting it), and print a count of word lengths, i.e. the total number of words of length 1 which occurred, the number of length 2, and so on.
Define a word to be a sequence of alphabetic characters. You should allow for word lengths up to 25 letters.
Typical output should be like this:
length 1 : 10 occurrences
length 2 : 19 occurrences
length 3 : 127 occurrences
length 4 : 0 occurrences
length 5 : 18 occurrences
....
To read characters to end of data see above question.
Here is my working solution,
#include<stdio.h>
int main(void){
char ch;
short wordCount[20] = {0};
int count = 0;
while(ch = getchar(), ch >= 0){
if(ch == ' ' || ch == ',' || ch == ';'|| ch == ':'|| ch == '.'|| ch == '/'){
wordCount[count]++;
count=0;
}else{
count++;
}
}
wordCount[count]++; // Incrementing here looks weird to me
for(short i=1; i< sizeof(wordCount)/sizeof(short); i++){
printf("\nlength %d : %d occurences",i, wordCount[i]);
}
}
Question:
1)
From code elegance aspect, Can I avoid incrementing(++) wordCount outside while loop?
2)
Can I make wordCount array size more dynamic based on word size, rather than constant size 20?
Note: Learnt about struct but am yet to learn dynamic structures like Linkedlist
For the dynamic allocations you can start with space for 20 shorts (although the problem statement appears to ask for you to allow for words up to 25 characters):
short maxWord = 20;
short *wordCount = malloc(sizeof(*wordCount) * maxWord);
Then, when you increment count you can allocate more space if the current word is longer than can be counted in your dynamic array:
} else {
count++;
if (count >= maxWord) {
maxWord++;
wordCount = realloc(sizeof(*wordCount) * maxWord);
}
}
Don't forget to free(wordCount) when you are done.
Since you don't need to count zero-length words, you might consider modifying your code so that wordCount[0] stores the number of words of length 1, and so on.
To 1):
maybe scan from one delimiting character to the next until you increment wordCount. Make EOF a delimiting character as well.
To 2)
you can scan the file twice and then decide how much memory you need. Or you dynamically realloc whenever the more memory is needed. This is something the std::array class does internally for example.
Also you should think about what happens if there are two characters after one another. Right now you would count this as a word.

simple string counter program debugging (pointers)

I am new to C programming and pointers.
I made a simple program where I can read in the string and the program tells you how many characters are there and how many alphabets had appeared how many times.
Somehow, my output is not right. I think it might be my pointer and dereferencing problem.
here is my main:
extern int* count (char* str);
int main(int argc, char* argv[])
{
int numOfChars =0;
int numOfUniqueChars = 0;
char str[80];
int *counts;
strcpy(str, argv[1]);
printf("counting number of characters for \"%s\"..\n", str);
printf("\n");
counts = count(str);
int j;
for (j =0; j<sizeof(counts); j++)
{
if(counts[j])
printf("character %c", *str);
printf("appeared %d times\n", counts[j]);
numOfChars++;
numOfUniqueChars++;
}
printf("\"%s\" has a total of %d character(s)\n", str, numOfChars);
printf(wow %d different ascii character(s) much unique so skill\n", numOfUniqueChars);
}
and this is my count function:
int* count(char* str)
{
int* asctb = malloc(256);
int numOfChars =0;
int i;
int c;
for(i = 0; i<strlen(str); i++)
c = str[i];
asctb[c]++;
numOfChars += strlen(str);
return asctb;
}
and when I compile and run it, my result comes up like this:
./countingCharacter doge
counting number of characters for "doge"...
appeared 0 times
appeared 0 times
appeared 0 times
appeared 0 times
"doge" has a total of 4 character(s)
wow 4 different ascii character(s) much unique so skill
But, I want my result to be like this:
Character d appeared 1 times
Character e appeared 1 times
Character g appeared 1 times
Character o appeared 1 times
"doge" has a total of 4 character(s)
wow 4 different ascii character(s) much unique so skill
Any help will be much appreciated.
Thanks in advance!
EDIT:
i added curly braces for my for loop in the main function.
now i get this result:
./countingCharacter doge
character # appeared 7912 times
character d appeared 1 times
character e appeared 1 times
character g appeared 1 times
character o appeared 1 times
why do I get that "#" in the beginning??
As #kaylum said, one particularly large issue is your use of braces. If you don't use braces with a control flow statement (if, for, while, etc.), only the next line is counted as a part of that statement. As such, this segment:
if (counts[j])
printf("character %c", *str);
printf("appeared %d times\n", counts[j]);
/* ... */
...will only execute the first printf if counts[j] != 0, but will unconditionally execute the following statements.
Your use of malloc is also incorrect. malloc(256) will only allocate 256 bytes; one int is generally 4 bytes, but this differs based on the compiler and the machine. As such, when malloc'ing an array of any type, it's good practice to use the following technique:
type *array = malloc(element_count * sizeof(type));
In your case, this would be:
int *asctb = malloc(256 * sizeof(int));
This ensures you have room to count all the possible values of char. In addition, you'll have to change the way you iterate through counts as sizeof (counts) does not accurately represent the size of the array (it will most likely be 4 or 8 depending on your system).
The variable numOfChars will not behave the way you expect it to. It looks to me like you're trying to share it between the two functions, but because of the way it's declared this will not happen. In order to give global access to the variable, it needs to be declared at global scope, outside of any function.
Also, the line:
printf("character %c ", *str);
...neither keeps track of what characters you've printed nor which you're supposed to, instead just repeatedly printing the first character. *str should be (char)j, since you're printing ASCII values.
That ought to do it, I think.
If you are new to C, there are a number of issues in your code you need to pay attention to. First, if a function returns a value, validate that value. Otherwise, from that point in your code on, you can have no confidence that it is actually operating on the value or memory location you think it is. For example, each of the following should be validated (or changed to stay within allowable array bounds):
strcpy(str, argv[1]);
int* asctb = malloc(256);
counts = count(str);
What if argv[1] had 100 chars? What if malloc returned NULL? How do you know count succeeded? Always include the necessary validations needed by your code.
While not an error, the standard coding style for C avoids caMelCase variables in favor of all lower-case. See e.g. NASA - C Style Guide, 1994 So
int numOfChars =0;
int numOfUniqueChars = 0;
could simply be nchars and nunique.
Next, all your if and for loop syntax fails to encapsulate the required statements in braces, e.g. {...} to create a proper block for your if or for. For example, the following:
for(i = 0; i<strlen(str); i++)
c = str[i];
asctb[c]++;
only loops over c = str[i]; and asctb[c]++; is only executed AFTER the loop exits.
You must initialize your variable, (especially your array elements) before you attempt to reference them otherwise undefined behavior results. (it could seem to work, give weird output like a strange "#" character, or segfault, that's why it is undefined). You have a big problem here:
int* asctb = malloc(256);
None of the values in asctb are initialized. So when you return the array to main() and loop over all values in the array, every element that was not explicitly assigned a value causes undefined behavior. You can either set all values to 0 with memset, or recognize when you need all values initialized and use calloc instead:
int *asctb = calloc (1, 256);
Avoid the use of "magic-numbers" in your code. 256 above is a great example. Don't litter you code with these magic-numbers, instead defined a constant for them at the beginning of your code with either #define or for numerical constants, use an enum instead.
Lastly, in any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed using free when it is no longer needed. You should validate your memory use by running your code though a Memory Error Checking Program, such as valgrind on Linux. It's simple to do and will save you from yourself more times than you can imagine.
Putting all these pieces together and fixing additional logic errors in your code, you look like you were attempting something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* constants for max characters in str and values in asctb */
enum { MAXC = 80, MAXTB = 128 };
int *count (char *str);
int main (int argc, char **argv) {
if (argc < 2) { /* validate str given as argument 1 */
fprintf (stderr, "error: insufficient input, usage: %s str.\n",
argv[0]);
return 1;
}
/* initialize all variables avoid CamelCase names in C */
char str[MAXC] = "";
int j = 0, nchars = 0, nunique = 0;
int *counts = NULL;
strncpy (str, argv[1], MAXC - 1); /* limit copy len */
str[MAXC - 1] = 0; /* nul-terminate str */
printf ("\ncounting number of characters for \"%s\"..\n\n", str);
if (!(counts = count (str))) { /* validate return */
fprintf (stderr, "error: count() returned NULL.\n");
return 1;
}
for (j = 0; j < MAXTB; j++)
if (counts[j]) {
printf ("character '%c' appeared: %d times\n",
(char)j, counts[j]);
nchars += counts[j];
nunique++;
}
free (counts); /* free allocated memory */
printf ("\n\"%s\" has a total of %d character(s)\n", str, nchars);
printf (" wow %d different ascii character(s) much unique so skill\n\n",
nunique);
return 0; /* main is a function of type 'int' and returns a value */
}
int *count (char *str)
{
if (!str) return NULL; /* validate str */
int *asctb = calloc (1, sizeof *asctb * MAXTB);
size_t i; /* you are comparing with size_t in loop */
if (!asctb) { /* validate memory allocation - always */
fprintf (stderr, "count() error: virtual memory exhausted.\n");
return NULL;
}
for(i = 0; i < strlen(str); i++)
asctb[(int)str[i]]++; /* array indexes are type 'int' */
return asctb;
}
(note: the first 30 characters in counts are in the non-printable range, see ASCIItable.com. The indexes were left as you had them, but note, in practice you may want to shift them unless you are interested in counting the non-printable \t, \n, etc. chars).
Example Use/Output
$ ./bin/ccount "address 12234"
counting number of characters for "address 12234"..
character ' ' appeared: 1 times
character '1' appeared: 1 times
character '2' appeared: 2 times
character '3' appeared: 1 times
character '4' appeared: 1 times
character 'a' appeared: 1 times
character 'd' appeared: 2 times
character 'e' appeared: 1 times
character 'r' appeared: 1 times
character 's' appeared: 2 times
"address 12234" has a total of 13 character(s)
wow 10 different ascii character(s) much unique so skill
Look over the logic and syntax corrections and let me know if you have any further questions.

c fgets char array

I have a problem with fgets. I can't save the String "temp" in the if-statement.
The Code:
1 #include <stdio.h>
2 #include <string.h>
3 #include<stdlib.h>
4
5 void moep(char** tmp){
6 FILE *fp;
7 fp=fopen("moep", "r");
8 int line_num = 1;
9 int find_result = 0;
10 char* str="jochen";
11 char temp[512];
12
13 while(fgets(temp, 512, fp) != NULL) {
14 if((strstr(temp, str)) != NULL) {
15 printf("A match found on line: %d \n", line_num);
16 tmp[find_result]=temp;
17 printf("\n%s\n", tmp[find_result] );
18 find_result++;
19 }
20 line_num++;
21 }
22 fclose(fp);
23 printf("tmp[0]:%s\n",tmp[0]);
24 tmp[1]="ich funktioniere";
25 }
26
27 int main(){
28
29 char* tmp[1];
30 moep(tmp);
31 printf("%s, %s\n",tmp[0],tmp[1]);
32 return 0;
33 }
and moep is:
unwichtig1
jochen g
unwichtig2
unwichtig3
The output:
/A match found on line: 2
jochen g
tmp[0]:unwichtig3
��Ł�, ich funktioniere
why can't I save "jochen" in tmp[0]?
Assigning to tmp[1] is assigning to random memory, as you're array isn't that big.
It will have all sorts of bad side effects - including crashing the program.
You are also reusing the temp[] array - so that will keep getting overwritten, you need to copy the string if you want to keep it.
There are several problems. I did not look at moep() at all, only main().
You probably did not intended to declare tmp as:
char* tmp[1];
This declares a pointer, but does not point anywhere. Accessing the second element in it is not "kosher" because it is declared to be one element. (But it doesn't matter because the array is not allocated.)
Probably you meant
char tmp[2];
This allocates a space for two characters.
It's a bit of a mess.
temp is an array of pointers to char. You assign the pointer to temp to the first, second etc. element of that array. That said, this is always the same value, since temp itself doesn't change, and it's contents gets overwritten every iteration of the loop.
Use strncpy() to copy from one string to another.

String of variable length to int array?

I have a string which is in the form
"1 2 8 4 9 0 " etc. I know it will follow the format of integer, space, integer...
However, I can't guarentee the length. I know the maximum length, if that helps.
I thought to use sscanf, but it needs a finite number of integers.
Any ideas how I can turn "1 2 3 " into an array where
A[0] = 1
A[1] = 2
A[2] = 3
Thanks!
strtol indicates where in the string it failed.
For a string like "1 2 3 ..." it will read the 1 and fail right afterwards; if you then pass that failure point (" 2 3 ...") to strtol it will read 2 and fail right afterwards, ..., ...
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char input[] = "6 82 -7453 4 0 63 ";
char *err = " ", *curr = input;
int x, k = 0;
while (*curr && isspace((unsigned char)*err)) {
x = strtol(curr, &err, 10);
printf("#%d: %d\n", k++, x);
curr = err + !!*err; // do not advance past the '\0'
}
return 0;
}
See it in action -- unlike the snippet above, the one at ideone initializes k with 1 :/ and doesn't deal well with inputs that end in whitespace.
Use
rc = sscanf(string, "%d%n", &array[i], &len);
In a loop. Test the rc, and use len to skip to the next item.
It's really not that complicated:
Calculate the length of the string with strlen
Allocate enough memory for the array with malloc
Iterate over the string again and populate the array
See it in action.
One of the many ways could be to use strtok to tokenize the string with the separator as space & the return value of each strtok call can be used with sscanf for single integer. As you know the maximum length you can iterate the array to be used with sscanf something on these lines.
Hope this helps!

Resources