strlen inconsistent with zero length string - c

I'm creating a DataStage parallel routine, which is a C or C++ function that is called from within IBM (formerly Ascential) DataStage. It is failing if one of the strings passed in is zero length. If I put this at the very first line of the function:
return strlen(str);
then it returns 0 for the calls that pass in empty values into str. If I put this at the first line, however...
if (strlen(str)==0) {return 0;}
then it does not return and goes into an infinite loop
I'm baffled - it works fine in a test harness, but not in DataStage.
Maybe there is something odd about the way DataStage passes empty strings to C routines?
int pxStrFirstCharList(char *str, char *chars )
{
if (strlen(str)==0) {return 0;}
if (strlen(chars)==0) {return 0;}
int i = 0;
//Start search
while (str[i]) //for the complete input string
{
if (strchr(chars, str[i]))
{
return i+1;
}
++i;
}
return 0;
}

There is a builtin function for what you are doing, it's called strcspn. This function takes two strings, and searches the first one for the first occurance of any of the characters of the second string.
I suggest using that than RYO...
http://www.cplusplus.com/reference/clibrary/cstring/strcspn/

How about this?
int pxStrFirstCharList(char *str, char *chars )
{
if (str && chars && (0 != strlen(str)) && (0 != strlen(chars)))
{
int i = 0;
//Start search
while (str[i]) //for the complete input string
{
if (strchr(chars, str[i]))
{
return i+1;
}
++i;
}
}
return 0;
}
Also, I don't quite get the point of the while loop ... (and no, I don't mean that this could be written as for). What I mean is that on one hand you are doing a search (strstr) that itself will be implemented as a loop and still you have some outer loop. Could it be that you actually wanted to have chars in its place, i.e.:
int pxStrFirstCharList(char *str, char *chars )
{
if (str && chars && (0 != strlen(str)) && (0 != strlen(chars)))
{
int i = 0;
//Start search
while (chars[i]) //for the complete input string
{
if (strchr(str, chars[i]))
{
return i+1;
}
++i;
}
}
return 0;
}
...? That is, look for each of the characters within chars inside the string denoted by str ...

If NULL is not explicitly part of the game, at least during development phase, it's always a good idea to add a precondition check on pointers received by a function:
int pxStrFirstCharList(char *str, char *chars )
{
if (!str)
return -1;
if (!chars)
return -2;
....
(The negative values -1 and -2 than tell the caller that something went wrong)
Or doing it in a more relaxed way, silently accepting NULL pointer strings as ""-string:
int pxStrFirstCharList(char *str, char *chars )
{
if (!str)
return 0;
if (!chars)
return 0;
...
If you are the only one using this API you could #ifndef BUILD_RELEASE these checks away for a release build if anything is tested stable.

I guess it is the strlen's issue when the length of the string is 0. For example,
char s1[0];
char *s2="a";
printf("%d %s\n", sizeof(s1), s1);//0 #
printf("%d %s\n", strlen(s1), s1);//3 #
printf("%d %s\n", sizeof(s2), s2);//8 a
printf("%d %s\n", strlen(s2), s2);// 1 a
You will get a weird answer for using strlen and you can check its source code in detail(https://code.woboq.org/userspace/glibc/string/strlen.c.html). In nutshell, you can use sizeof instead of strlen for char string or avoid 0 length case by using strlen.

Related

Function to reverse print

Yesterday I had to solve an exam exercise, which, unfortunately, I failed..
The exercise was to create a function in C with the following rules:
Write a function that takes a string and displays the string in reverse
order followed by the newline.
Its prototype is constructed like this : char *ft_rev_print (char *str)
It must return its argument
Only allowed to use function 'write'(so no printf or others)
With that information I wrote :
int ft_strlen(char *str) /*to count the length of the original string*/
{
int i;
i = 0;
while (str[i])
i++;
return (i);
}
char *ft_rev_print (char *str)
{
int i;
i = ft_strlen(str);
while (i)
{
write (1, (str +1), 1);
i--;
}
return (str); /*returning its argument */
}
int main(void) /*IT HAD TO WORK WITH THIS MAIN, DID NOT WROTE THIS MYSELF!*/
{
ft_rev_print("rainbow dash");
write(1, "\n", 1);
return (0);
}
I tried for ages to get it to work, but failed.. So now I'm breaking my head over this. What did i do wrong ? What did i miss?
Thanks in advance !
Your while loop is wrong, you start with i=0 and iterate while it's not zero, so no iterations will be done.
What you should do is:
initialize i so that it is the index of the last character
loop as long as it's a valid index
print the i-th character, not always the second (at index one)
char *ft_rev_print (char *str)
{
int i;
i = ft_strlen(str) - 1; // <-- Initialize i to be the index of the last character
while (i >= 0) // <-- Loop as long as it's valid
{
write (1, (str+i), 1); // <-- Print the i-th character
i--;
}
return (str);
}
For starters your teachers are not enough qualified. The functiuon should be declared at least like
char * ft_rev_print( const char *str );
^^^^^
because the passed string is not changed within the function.
You forgot to call the function ft_strlen.
It seems you mean
i = ft_strlen( str );
As a result this loop
i = 0;
while (i)
{
//...
}
is never executed because initially i is equal to 0 and the condition of the loop at once evaluates to false.
Also in this call
write (1, (str +1), 1);
^^^^^^^^
you are always trying to output the second symbol of the string.
Also the output of the new line character '\n' should be within the function according to its description.
The function can look the following way as it is shown in the demonstrative program below (instead of the non-standard function write I will use the function putchar but you can substitute it for write yourself)
#include <stdio.h>
char * ft_rev_print( const char *str )
{
const char *p = str;
while ( *p ) ++p;
while ( p != str ) putchar( *--p ); // substitute the call of putchar for write( 1, *--p, 1 )
putchar( '\n' ); // substitute the call of putchar for write( 1, "\n", 1 )
return ( char * )str;
}
int main(void)
{
ft_rev_print( "rainbow dash" );
return 0;
}
The program output is
hsad wobniar
Hey I have tried your question and there is a small point I would like to add,In your question you have written the length of your string and also the part below in your code:
write (1, (str +1), 1);
was not correct so I corrected it and in that basically we are adding the back of previous characters like this and error was in a while loop condition:
write (1,(str+i),1);
You can full prototype here:
char *ft_rev_print (char *str)
{
int i;
i = ft_strlen(str);//returning the length of string
while (i>=0)
{
write (1,(str+i),1);
i--;
}
return (str); /*returning its argument */
}

How to get palindromes using pointer in c?

I apologize that I cannot use English fluently because I am not a English speaker.
I want to get palindromes such as "A man, a plan, a canal, Panama!" or "Was it a car or a cat I saw?"
I studied Pointer in C today. So I made code to get palindromes.
#include <ctype.h>
/**
* #brief : Check if the given str is a palindrome
* #return: 1 if the given str is a palindrome, zero otherwise
* #param : str - pointers to the null-terminated byte string to check
*/
int ispalindrome(char *str)
{
int i, j;
char *pal;
for(i=0 ; *str!=0; i++) {
if(ispunct(*str)){ //if *str is punctuation mark, do not anything.
str++;
}
else if(isupper(*str)){ //if *str is a capital letter, change it to small letter and *pal=*str
*pal = tolower(*str);
str++;
pal++;
}
else //if *str is a small letter or a digit, *pal=*str
*pal = *str;
str++;
pal++;
}
for(j=0; pal[j]==pal[i-(j+1)] && j<=i/2; j++) //compare pal
;
if(j>i/2)
return 1;
return 0;
}
int main(void)
{
char buffer[1024];
int i;
gets(buffer);
printf("[%s] is ", buffer);
if (ispalindrome(buffer))
puts("a palindrome");
else
puts("not a palindrome");
return 0;
}
However, there is logical error.
When I input anything, the output is always palindrome.
I don't know where are errors.
For example,
for get "A man, a plan, a canal, Panama!", I removed(disregarded) 'space' and 'punctuation marks', changed capital letters to small letter. (amanaplanacanalpanama)
However, when i input like 'abcdef', I got also "[abcedf] is a palindrome."
I am not here judging the efficiency of your code (see the comments for some suggestions on how to improve your algorithm), but there are a number of 'simple' errors in your code that can be fixed.
The code below is a minimal modification of your own that works, with triple-slash (///) comments where I've made changes:
#include <stdio.h>
#include <ctype.h>
int ispalindrome(char* str)
{
int i, j, k = 0; /// We need "k" to count the length of our PROCESSED string
char testBuffer[1024]; /// We need an ACTUAL BUFFER for our processed string ...
char* pal = &testBuffer[0];/// ... and start with "pal" pointing to the first element!
for (i = 0; *str != 0; i++) {
if (ispunct(*str) || isspace(*str)) { ///if *str is punctuation mark OR A SPACE, do not anything.
str++;
}
else if (isupper(*str)) { //if *str is a capital letter, change it to small letter and *pal=*str
*pal = tolower(*str);
str++;
pal++;
++k; /// Increase length of test string!
}
else { ///if *str is a small letter or a digit, *pal=*str
*pal = *str;
str++;
pal++;
++k; /// Increase length of test string!
}/// I think you forgot to include the { and }!
}
/// You left 'pal' pointing to a yet-to-be-filled character ...
pal = &testBuffer[0];/// We MUST reset "pal" to point at the BEGINNING of testBuffer!
/// "k" is the length of our processed string … NOT "i"...
for (j = 0; pal[j] == pal[k - (j + 1)] && j <= k / 2; j++) //compare pal
;
if (j > k / 2) return 1;/// Again, use "k" not "i"
return 0;
}
int main(void)
{
char buffer[1024];
// int i; /// This is never used!
// gets(buffer); /// This is an obsolete (and dangerous) function...
fgets(buffer, 1024, stdin); /// Use this instead!
printf("[%s] is ", buffer);
if (ispalindrome(buffer))
puts("a palindrome");
else
puts("not a palindrome");
return 0;
}
When you understand the reasons behind the required changes, I may then post an 'appendix' that suggests some efficiency/style improvements.
Appendix:
OK, you state in your question that you are studying pointers in C, so here's a version of your code that may give you some idea of what can and cannot be done with character string pointers. I don't claim that it is the most efficient method possible, but will hopefully help you understand the underlying concepts:
int ispalindrome(const char* str) // Not required, but use of "const" is good to prevent accidentally changing
{ // something you shouldn't be changing (see next comment)
char* testBuffer = malloc(strlen(str)); // It maybe tempting to just re-use the given "str" buffer, but doing
if (testBuffer == NULL) return 0; // so would spoil any later display by the calling program!
// We should always check the return value of "malloc" but what do do in case of error is up to you
char* lastTest = testBuffer; // Keeps track of the index to the last character in test string
while (*str) // We could use while (*str != 0) but this does the same and is more succinct!
{
if (isalnum(*str)) // Rather than check for what we don't what, check for what we do want ...
{ // The "isalnum" function is TRUE for any letter or digit, FALSE otherwise
*lastTest++ = tolower(*str); // The "tolower" funtion will leave uppercase and digits unchanged
// Note: The "++" (post-increment) operator will increase "lastTest" AFTER we have assigned to it
}
++str; // Move to the next character in our input string
}
// At this point, "lastTest" points to one beyond the last character in our processed string. We can loop using
// this as a pointer to our 'right-hand' compare (after we decrement it) and a 'local' pointer to our 'left-hand'
// compare (initialised to the first element of "testBuffer") ...
char* lhCompare;
for (lhCompare = testBuffer; (--lastTest > lhCompare) && (*lastTest == *lhCompare); ++lhCompare)
;
// Note: It is perfectly 'correct' and legal in C to compare two pointers to the same type! So, rather than
// keeping track of string lengths and indexes, we can just compare the 'lastTest' and 'rhCompare' addresses
// to see if we have reached/crossed the middle of the test string.
free(testBuffer); // Always call "free" to release memory allocated by "malloc" when you're done with it
// In the test below, we can still use the pointer values, even though the memory they refer to is no longer
// valid. But we CANNOT dereference them, with something like: char a = *lhCompare to get the 'middle' letter!
return (lhCompare >= lastTest); // Comparison will be TRUE (= 1) if our letters all match, FALSE (0) otherwise
}
As before, please feel free to ask for any further clarification and/or explanation of the code or concepts used.

void function that removes al the non alphabet chars

I am trying to write a program that gets several strings until it gets the 'Q' string (this string basically stops the scanf).
Each one of the strings is sent to a function that romoves everything except the letters. For example if I scan 'AJUYFEG78348' the printf should be 'AJUYFEG'.
The problem is that the function has to be void.
I have tried several ways to make the "new array with only letters" printed, but none of them worked.
(Is is not allowed to use strlen function)
#include <stdio.h>
void RemoveNonAlphaBetChars(char*);
int main()
{
int flag=1;
char array[100]={0};
while (flag == 1)
{
scanf("%s", &array);
if(array[0] == 'Q' && array[1] =='\0') {
flag=0;
}
while (flag == 1)
{
RemoveNonAlphaBetChars(array);
}
}
return 0;
}
void RemoveNonAlphaBetChars(char* str)
{
int i=0, j=0;
char new_string[100]={0};
for (i=0; i<100; i++)
{
if (((str[i] >= 'a') && (str[i] <= 'z')) || ((str[i] >= 'A') && (str[i] <= 'Z')))
{
new_string[j] = str[i];
j++;
}
}
printf("%s", new_string);
return;
}
The fact that the function has only one argument, non-const char pointer, hints at the fact that the string is going to be changed in the call (better document it anyway), and it's perfectly all right.
A few fixes to your code can make it right:
First, don't loop to the end of the buffer, just to the end of the string (without strlen, it's probably faster too):
for (i=0; str[i] != '\0'; i++)
then don't forget to nul-terminate the new string after your processing:
new_string[j] = '\0';
Then, in the end (where you're printing the string) copy the new string into the old string. Since it's smaller, there's no risk:
strcpy(str,new_string);
now str contains the new stripped string.
Another approach would be to work in-place (without another buffer): each time you encounter a character to remove, copy the rest of the string at this position, and repeat. It can be inefficient if there are a lot of characters to remove, but uses less memory.
The key here is that you are never inserting new characters into the string. That guarantees that the input buffer is large enough to hold the result. It also makes for an easy in-place solution, which is what the void return type is implying.
#include <ctype.h>
#include <stdio.h>
...
void RemoveNonAlphaBetChars(char* str)
{
char *from, *to;
for(from = to = str; *from; from++) {
if(isalpha(*from)) {
if(from > to) *to = *from;
to++;
}
}
*to = *from;
printf("%s\n", str);
return;
}
The pointer from steps along the string until it points to a NUL character, hence the simple condition in the loop. to only receives the value of from if it is a character. The final copy after the loop ensures NUL termination.
Update
If you are dealing with 1) particularly large strings, and 2) you have long stretches of letters with some numbers in between, and 3) your version of memmove is highly optimized compared to copying things manually (e.g. with a special processor instruction), you can do the following:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
...
void RemoveNonAlphaBetChars(char* str)
{
char *from, *to, *end;
size_t len;
for(from = to = str; *from; from = end) {
for(; *from && !isalpha(*from); from++) ;
for(end = from; *end && isalpha(*end); end++) ;
len = end - from;
if(from > to) {
if(len > 1) {
memmove(to, from, len);
} else {
*to = *from;
}
}
to += len;
}
*to = *end;
printf("%s\n", str);
return;
}
The general idea is to find the limits of each range of letters (between from and end), and copy into to block by block. As I stated before though, this version should not be used for the general case. It will only give you a boost when there is a huge amount of data that meets particular conditions.
void return type is a common approach to making functions that produce C string results. You have two approaches to designing your API:
Make a non-destructive API that takes output buffer and its length, or
Make an API that changes the the string in place.
The first approach would look like this:
void RemoveNonAlphaBetChars(const char* str, char *result, size_t resultSize) {
...
}
Use result in place of new_string, and make sure you do not go past resultSize. The call would look like this:
if (flag == 1) { // if (flag == 1), not while (flag == 1)
char result[100];
RemoveNonAlphaBetChars(array, result, 100);
printf("%s\n", result);
}
If you decide to use the second approach, move printf into main, and use strcpy to copy the content of new_string back into str:
strcpy(str, new_string);

Calculating length of a string in C

int strlength(const char *myStr){
//variable used for str length counter
int strlength = 0;
//loop through string until end is reached. Each iteration adds one to the string length
while (myStr[strlength] != '\0'){
putchar(myStr[strlength]);
strlength++;
}
return strlength;
}
Why will this not work as intended? I just want to find the length of a string.
From a comment on another answer:
I am using fgets to read in a string. and i have checked to make sure that the string that was typed was stored correclty
In that case, there is a trailing newline stored, so your function computes
strlength("hello\n")
The code is correct, you just didn't pass it the input you believed to pass.
More reliable version:
size_t strlength(const char * myStr)
{
return strlen(myStr);
}
You can try this also:-
int string_length(char *s)
{
int c = 0;
while(*(s+c))
c++;
return c;
}
No need to worry about fgets() and removing the trailing \n.
while (myStr[strlength] != '\0'){
putchar(myStr[strlength]);
strlength++; //When mysStr[strlength] == \0, this line would have already incremented by 1
}
Quick fix:
return (strlength-1);//This will return correct value.
A more better approach:
int strlen(const char *s)
{
char *str=s;
while(*str)
{
str++;
}
return (s-str);
}

Reversing an unbounded string without checking for null character

Recently in an interview, I was asked to write a code to reverse an unbounded string without checking for the null character. I don't know the length of the string and cannot use any library function in any form..
I wrote the following code ( which I know is incorrect due to many reasons, one being I am not terminating the string with '\0', instead I am terminating it with CTRL+Z or CTRL+D)
while(scanf("%c",arr[i++])!=-1);
for(i--;i>=0;i--)
puts(arr[i]);
What can be the possible algorithm !!
Maybe something like:
void
reverse_print(const char* string) {
if (*string == 0) {
return;
}
reverse_print(string + 1);
putchar(*string);
}
Or, reading the string from input:
void
reverse_print() {
char c;
if (1 != scanf("%c", &c)) {
return;
}
reverse_print();
putchar(c);
}
It seems you are reading the string from input, and trying to print it.
You can just count the number of characters, and treat it like it was a normal array, something along the lines of:
int main() {
int i = 0;
char arr[256];
while(scanf("%c",arr + (i++))!=-1);
for(i = i-1; i+1; i--) putchar(arr[i]);
return 0;
}
If the array is completely unbounded, you will have to make sure you are not out of space, and if you are - reallocate it.
You can also add a NULL terminator manually when you are done. Since, it is not checking for it, you should be fine with the requirements.
#include <stdio.h>
int main(void)
{
char* string1 = "This is me the right guy!! understood !!!";
char* string = string1;
char *nonstring = "\x01\x05\x0a\x15";
int len = 0;
while ((*string ^ nonstring[0]) && (*string ^ nonstring[1])
&& (*string ^ nonstring[2]) && (*string ^ nonstring[3])
&& (*string ^ nonstring[4]))
{
len++;
string++;
}
//printf("%d\n", len);
while(len)
{
printf("%c", string1[--len]);
}
return 0;
}

Resources