How to properly change elements in a char [] in c - c

I am trying to create a function that takes in word from a file then normalizes it by deleting any punctuation or quotes. The problem I am having is that when I have a word that has " in the front or both the front and back like "how", I get out put like this:
"howw"
howwETX
"well
"wel
How do I stop this from happening?
char *normalize( int count, char entry[], char output[] ){
count--;/* to allow for the zero index of an array*/
if( entry[0] == '"' && entry[count] == '"' ){
for(int i=1 , j=0;j < count - 1; i++,j++ ){
output[j] = entry[i];
}
output[count + 1 ] = '\0';
return output;
}else if( entry[0] == '"' ){
for(int i = 0 , j=0; j < count; i++, j++ ){
output[j] = entry[i];
}
output[count++] = '\0';
return output;
} else if( entry[count] == '"' || ispunct( entry[count] ) ){
for(int i=0 , j=0;i < count; i++,j++ ){
output[j] = entry[i];
}
output[count] = '\0';
return output;
}
return entry;
}/* ends normalize( int count, char entry[], char output[] )*/

Let's play debugger. Suppose your input is this:
entry == "a"
count == 3
We begin executing, and here's what happens:
count == 2
i == 1
j == 0
output[0] == 'a'
The loop exits and then:
output[3] == '\0'
So now output contains aa", which is incorrect. The solution is to set output[count-1] to null when the loop exits, not count + 1.

You are not counting your indices correctly.
In the case where there are quotes front and back, the length of the output string is count - 1, after count has been updated, so you should set output[count - 1] = '\0' rather than output[count + 1] = '\0'. In fact, you may want to decalre j outside your loop and use output[j] since that will be the correct position in the string (in all three cases).
In the case where only the first character is a quote, you should skip the quote: i = 1, not i = 0.

Try the following. At least it is correct and looks simpler.:)
char * normalize( char output[], const char entry[], size_t n )
{
if ( n != 0 && entry[0] == '"' )
{
++entry;
--n;
}
if ( n != 0 && ispunct( entry[n-1] ) )
{
--n;
}
strncpy( output, entry, n );
output[n] = '\0';
return output;
}

Related

what's wrong with my code, it doesn't print the third sentence

Write a program that allows the user to enter a sentence. The program should then print the sentence starting with the third word.
For example it should print like:
Enter a sentence: Welcome to the course in programming
the course in programming
The problem with my code is that it only print the sentence starting with the second word instead of the thrird.And I wonder what is wrong with my code?
#include <stdio.h>
#include <string.h>
#define SIZE 100
int main(void)
{
char arr[SIZE];
char* p = NULL;
int count = 0;
printf("Enter a sentence:");
fgets(arr, SIZE, stdin);
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
}
if (count < 3 ) {
printf("The sentence is to short!\n");
}
else {
count = 0;
for (int i = 0; i < strlen(arr); i++) {
if (arr[i] == ' ') {
count++;
}
if (count == 2) {
p = &arr[i + 1];
}
}printf("%s\n", p);
}
return 0;
}
I do not know why the program outputs the sentence from the second word instead of the third word but in any case this for loop
for (int i = 0; i < strlen(arr); i++) {
if (arr[i] == ' ') {
count++;
}
if (count == 2) {
p = &arr[i + 1];
}
}
is incorrect because when count is equal to 2 then for each character in the string that is not a space the value of p is changed due to this if statement
if (count == 2) {
p = &arr[i + 1];
}
At least you need to insert a break statement like
if (count == 2) {
p = &arr[i + 1];
break;
}
Also the first for loop
for (int i = 0; i < SIZE; i++) {
if (arr[i] == ' ') {
count++;
}
}
invokes undefined behavior because instead of SIZE you need to use strlen( arr ).
And moreover you need to remove the trailing new line character '\n' from the string before calculating the number of words.
Apart from that the approach in any case is incorrect because it will not work when a string contains adjacent spaces.
You're trying to do to much with too much. Simplifying the code makes life easier.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define SIZE 100
int main(void) {
printf( "Enter a sentence:" );
/* ONE buffer to hold the input */
char arr[SIZE] = { 0 }; // define (and initialise) variables close to use
fgets( arr, SIZE, stdin );
/* ONE counter (index) to traverse the input */
int i = 0;
while( arr[i] && isspace( arr[i] ) ) i++; // skip over leading whitespace (not considered)
while( arr[i] && !isspace( arr[i] ) ) i++; // skip over the 1st "word" (if present )
while( arr[i] && isspace( arr[i] ) ) i++; // skip over intervening whitespace (if present )
while( arr[i] && !isspace( arr[i] ) ) i++; // skip over the 2nd "word" (if present )
while( arr[i] && isspace( arr[i] ) ) i++; // skip over intervening whitespace (if present )
if( arr[i] )
printf( "%s\n", arr + i );
else
printf("The sentence is to short!\n");
return 0;
}
Enter a sentence: Now is the time for all good men
the time for all good men
You can see the repetitive code (testing for spaces, then testing for non-spaces.) This could be "packaged-up" in a function that takes a couple of parameters. I'll leave that as an exercise. (Perhaps look into strpbrk() strspn() and strcspn()...)

Heap buffer overflow caused by single line

Currently trying to work on my C (very new to it) by doing some leetcode questions. I'm puzzled by this issue, as it gives me a heap buffer overflow but only because of a single line. interpret() is called and passed a string command where 1 <= command.length <= 100, and will consist of "G", "()", and/or "(al)" in some order, with no other characters appearing.
char * interpret(char * command){
char * ret = malloc(sizeof(char) * 100);
int counter = 0;
for(int i = 0; i < sizeof(command) - 1; i++)
{
if(command[i] == 'G')
{
ret[counter] = 'G';
counter ++;
}
else if(command[i] == '(')
{
if (command[i + 1] == ')')
{
ret[counter] = 'o';
counter ++;
}
else
{
//ret[counter] = 'a'; ***********
ret[counter + 1] = 'l';
counter += 2;
}
}
ret[counter] = '\0';
}
return realloc(ret, counter * sizeof(char));
}
If the starred line is uncommented, then the entire program crashes in leetcode, but works fine on VSCode and returns the correct solution. I would appreciate any help, I'm sure it's something small I'm missing. Thanks.
ETA: Here is the leetcode problem in question
The parameter command has the pointer type char *.
So the operator sizeof applied to the pointer yields the size of the pointer instead of the length of the pointed string
for(int i = 0; i < sizeof(command) - 1; i++)
You could just write
for( size_t i = 0; command[i] != '\0'; i++)
Also it is unclear why there is used the magic number 100
char * ret = malloc(sizeof(char) * 100);
You could at first count the result characters and then allocated an array of the appropriate size and fill it.
Moreover due to this statement
ret[counter] = '\0';
(that is also unclear why it is within the for loop) you need to allocate an array with counter + 1 characters instead of counter characters as you are doing
return realloc(ret, counter * sizeof(char));
A straightforward approach can look the following way as shown in the demonstration program below.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * interpret( const char *command )
{
size_t n = 0;
for ( size_t i = 0; command[i] != '\0'; )
{
if ( command[i] == 'G' )
{
++n;
++i;
}
else if ( strncmp( command + i, "()", 2 ) == 0 )
{
++n;
i += 2;
}
else if ( strncmp( command + i, "(al)", 4 ) == 0 )
{
n += 2;
i += 4;
}
else
{
++i;
}
}
char *result = malloc( n + 1 );
if ( result != NULL )
{
n = 0;
for ( size_t i = 0; command[i] != '\0'; )
{
if ( command[i] == 'G' )
{
result[n++] = 'G';
++i;
}
else if ( strncmp( command + i, "()", 2 ) == 0 )
{
result[n++] = 'o';
i += 2;
}
else if ( strncmp( command + i, "(al)", 4 ) == 0 )
{
result[n++] = 'a';
result[n++] = 'l';
i += 4;
}
else
{
++i;
}
}
result[n] = '\0';
}
return result;
}
int main( void )
{
char *s = interpret( "G()(al)" );
if ( s ) puts( s );
free( s );
s = interpret( "(al)G(al)()()G" );
if ( s ) puts( s );
free( s );
}
The program output is
Goal
alGalooG
Pass the size of command to interpret.
In C, when you pass a string to a function, you’re not actually passing the full string, you’re passing a pointer to the first element in the string. This becomes an issue when you do sizeof(command), as you're just getting the size of the pointer and not the full string. If you try to loop over this string as done in the question, this can either lead to an underread, if you have a string longer than sizeof(char*), or a buffer overflow, if you have a string shorter than sizeof(char*). Generally, you shouldn’t use sizeof on pointers.
To fix your code, use strlen on the string you're passing to command in the calling function and do something similar to this:
char * interpret(char * command, int size){
char * ret = malloc(sizeof(char) * 100);
int counter = 0;
for(int i = 0; i < size; i++)
{ ...

Output a string with a new character in each line, forming a right triangle

Trying to output a string that has been assigned to an array, with different characters on each new line, eventually creating a right triangle. But I'm completely stuck. I believe some for loops should be involved to iterate over each character but I don't know how to increase the array index on each new line to output one character more than the line before.
This is a sketch that allowed me to visualize this:
string[0]
string[1] + string[2]
string[3] + string[4] + string[5]
string[6] + string[7] + string[8] + string[9]
For example, let's take into account this line of code: char string[50] = "Assignment";
The output desired would look like this:
A
s s
i g n
m e n t
Any guidance would be appreciated.
You can do it using only one while loop.
Here is a demonstrative program
#include <stdio.h>
void triangle_output( const char *s )
{
size_t n = 1;
size_t i = n;
while ( *s )
{
if ( i-- == 0 )
{
putchar( '\n' );
i = n++;
}
putchar( *s++ );
putchar( ' ' );
}
if ( i != n - 1 ) putchar( '\n' );
}
int main(void)
{
char *s = "Assignment";
triangle_output( s );
return 0;
}
The program output is
A
s s
i g n
m e n t
Or the function can be rewritten the following way
void triangle_output( const char *s )
{
size_t n = 1;
size_t i = 0;
while ( *s )
{
putchar( *s++ );
putchar( ' ' );
if ( ++i == n )
{
putchar( '\n' );
i = 0;
++n;
}
}
if ( i != 0 ) putchar( '\n' );
}
This function will print the triangle. If the string have enough chars to print the whole last line it will print -.
void printTriangle(const char *s)
{
size_t length = strlen(s), pos = 0;
for(size_t line = 1; line < -1; line++)
{
for(size_t ch = 1; ch <= line; ch++)
{
if(pos < length) printf("%c", *s++);
else printf("-");
pos++;
}
printf("\n");
if(pos >= length) break;
}
}
int main(void)
{
printTriangle("Assignment123");
}
https://godbolt.org/z/xxM1eEY6a

Stuck Finding Bug in LeetCode Question Solution

The problem is simple: take a string, and reverse the position of ONLY letters (lower or uppercase). Leave any special characters where they are. My solution:
char * reverseOnlyLetters(char * S){
int Len = strlen(S);
char *StrBeg, *StrEnd, tempCh;
bool FoundStart = 0, FoundEnd = 0;
StrBeg = S;
StrEnd = S + (Len - 1);
for (int i = 0; i < (Len/2); i++)
{
if (((*StrBeg>= 'A') && (*StrBeg <= 'Z')) || ((*StrBeg >= 'a') && (*StrBeg <= 'z')))
{
FoundStart = 1;
}
else
{
StrBeg++;
}
if (((*StrEnd >= 'A') && (*StrEnd <= 'Z')) || ((*StrEnd >= 'a') && (*StrEnd <= 'z')))
{
FoundEnd = 1;
}
else
{
StrEnd--;
}
if(FoundStart && FoundEnd)
{
tempCh = *StrEnd;
*StrEnd = *StrBeg;
*StrBeg = tempCh;
StrBeg++;
StrEnd--;
FoundStart = 0;
FoundEnd = 0;
}
}
return S;
}
The issue is a testcase like "a-bC-dEf-ghIj" fails; the "E" and the "f" in the middle either don't get swapped at all, or (as I suspect), get swapped, but then get swapped BACK. Anyone see what I'm doing wrong?
Problem is this for (int i = 0; i < (Len/2); i++).
If lenght of string is even, it's ok, but in case it's even it doesn't go through the middle character. E in this "a-bC-dEf-ghIj" case, so it can't switch it with f.
The approach using this for loop
for (int i = 0; i < (Len/2); i++)
is incorrect. Let's assume that the string is "#AB". The result string will look like "#BA"
But using your loop you will have (as Len / 2 is equal to 1)
for (int i = 0; i < 1; i++)
In the first and single iteration of the loop the pointer StrBeg will be incremented
StrBeg++;
because the pointed character is not a letter.
So nothing will be reversed.
The function can be written simpler the following way
#include <stdio.h>
#include <string.h>
#include <ctype.h>
char * reverse_letters( char *s )
{
for ( char *first = s, *last = s + strlen( s ); first < last; ++first )
{
while ( first != last && !isalpha( ( unsigned char )*first ) ) ++first;
while ( last != first && !isalpha( ( unsigned char )*--last ) );
if ( first != last )
{
char c = *first;
*first = *last;
*last = c;
}
}
return s;
}
int main( void )
{
char s[] = "one, two, three";
puts( s );
puts( reverse_letters( s ) );
char s1[] = "#AB";
puts( s1 );
puts( reverse_letters( s1 ) );
}
The program output is
one, two, three
eer, hto, wteno
#AB
#BA

How to transfer a char * into a 2d char array

I am trying to take a char *, which represents a single word and i got the char * from a function, and put it into a 2d array but instead it just repeats the first letter of each word several times. the output looks like this
ttttttttttttttttttttttttttttttttvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvoooooooooooooooooooooooooo
and my input file is
three
versions
of
simple
with
spell
check
checking
program
we
will
write
I am not sure how to correctly transfer a char * into a 2d array
here are all the functions i am using
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include "dict.h"
bool is_valid_entry( int strLength, char entry[] ){
if( entry[0] != '"' && !isalpha(entry[0]) && entry[0] != '\'' ){
return false;
}
strLength--; /* to allow for the zero index of the array*/
for ( int i=1; i < strLength; i++ )
if ( !isalpha( entry[i] ) ){
return false;
}
if (!isalpha( entry[strLength] ) && !ispunct( entry[strLength] ) && entry[strLength] != '"' && entry[strLength] != '\'' ){
return false;
}
return true;/* if the entry passes all of the tests the function will return true meaning the the word is valid*/
}/* ends is_valid_entry( int strlength, char entry[] )*/
char * normalize( int strLength, char entry[], char output[] ){
strLength--;/* to allow for the zero index of an array*/
int j = 0;
for( int i = 0;i < strLength; i++){/* converts all of the elements in the entry[] to lower case*/
if (isupper( entry[i] ) ){
entry[i] = tolower( entry[i] );
}
}
if( ( entry[0] == '"' && entry[strLength] == '"' ) || (entry[0] == '\'' && entry[strLength] == '\'' ) ) {
for(int i=1 ; i < strLength ; i++,j++ ){
output[j] = entry[i];
}
output[j] = '\0';/* removes the last character which is either a '"' ir a '\''*/
return output;/* returns the noramlized word*/
}else if( entry[0] == '"' || entry[0] == '\'' ){
for(int i = 1; j < strLength; i++, j++ ){/*i=1 in order to skip the first element in the entry arrary*/
output[j] = entry[i];
}
output[j] = '\0';
return output;/* returns the noramlized word*/
} else if( entry[strLength] == '"' || ispunct( entry[strLength] ) || entry[strLength] == '\''){
for(int i = 0;j < strLength; i++,j++ ){
output[j] = entry[i];
}
output[j] = '\0';
return output;/* returns the noramlized word*/
}
return entry;/* returns the original array since it does not need to be normalized*/
}/* ends normalize( int strlength, char entry[], char output[] )*/
void load_dict(char *fileName, char dictionary[30000][31]) {
FILE *fdict;
fdict = fopen( fileName, "r" );/* file pointer for the dictionary file*/
char *normDictWord;
char normDict [33];
int strLength,i,j,ch;
if ( fdict == NULL ){
fprintf(stderr,"Could not open file: %s\n", fileName );/*checks to make sure the dictionary file can be opened*/
exit(1);
}
for (i = 0; (ch = fgetc( fdict ) ) != EOF; i++ ) {
char word[33] = "";/* resets the array*/
for (strLength = 0; !isspace( ch ) ; strLength++ ){
word[strLength] = ch;
ch = fgetc( fdict );
}
if (is_valid_entry( strLength , word ) ){
normDictWord = normalize( strLength , word , normDict );/*normalize then do the linear search then print the word if it is not found in the dictionary*/
for(j = 0; j <= 31;j++){
dictionary[i][j] = * normDictWord ;
printf("%c",dictionary[i][j]);
}
}
}
fclose( fdict );
}
I cannot completely understand your code, fix formatting, and add some parts that are missing (otherwise I can't test it). However:
dictionary[i][j] = * normDictWord;
should be something like:
dictionary[i][j] = * (normDictWord + j);
or equivalently:
dictionary[i][j] = normDictWord[j] ;
Key problem (#pez)
for (j = 0; j <= 31; j++) {
// dictionary[i][j] = *normDictWord;
dictionary[i][j] = normDictWord[j];
printf("%c", dictionary[i][j]);
}
Some other problems:
Original code will loop endlessly should the EOF condition occur before a space does. isspace(EOF) is false. This loop also does not prevent overrun.
char word[33] = "";
// for (strLength = 0; !isspace(ch); strLength++) {
for (strLength = 0; !isspace(ch) && ch != EOF && strLength < sizeof word; strLength++) {
word[strLength] = ch;
ch = fgetc(fdict);
}
The following does not "converts all of the elements in the entry[] to lower case", only ellements [0 ]... [original_string_length-2]. Maybe code should do strLength--; after the loop.
strLength--;/* to allow for the zero index of an array*/
...
for (int i = 0; i < strLength; i++) {/* */
...

Resources