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.
Related
So I have a function that takes a string, and strips out special format characters, and assigns it to another string for later processing.
A sample call would be:
act_new("$t does $d");
It should strip out the $t and the $d and leave the second string as " does ", but its not assigning anything. I am getting back into programming after quite a few years of inactivity, and this is someone elses code (A MUD codebase, Rom), but I feel like I am missing something fundamental with pointer assignments. Any tips?
(This is truncated code, the rest has no operations on str or point until much later)
void act_new(const char *format)
{
const char *str;
char *point;
str = format;
while ( *str != '\0' ) {
if ( *str != '$' ) {
*point++ = *str++;
continue;
}
}
}
You need to increment str every time through the loop, not only when you assign to point. Otherwise you end up in an infinite loop when the character doesn't match the if condition.
You also want to skip the character after $, so you have to increment str twice when you encounter $.
The code is simpler if you use a for loop and array indexing rather than pointer arithmetic.
size_t len = strlen(format);
for (size_t i = 0; i < len; i++) {
if (format[i] == '$') {
i++; // extra increment to skip character after $
} else {
*point++ = format[i];
}
}
There are a few problems with your code, as pointed out in the comments:
point is not initialized (garbage pointer value)
continue doesn't do anything
infinite loop if a $ is encountered
When writing the function, one must also keep in mind to skip an extra character if a $ is encountered if and only if it's not the last character in the string (except for the '\0').
Since you know how many times you need to loop, a for loop is better suited and, as a bonus, you don't have to explicitly check if the character after a $ is '\0' when skipping an extra character in format (renamed src below). Also, don't forget to terminate the destination string.
This code will take care of those things for you:
void act_new(const char *src)
{
const size_t length = strlen(src);
char * const dst = (char*)malloc(sizeof(char)*(length+1));
if(dst == NULL)
// Error handling left out
return;
char *point = dst;
for(size_t i = 0; i < length; ++i)
{
if(src[i] == '$')
{
++i;
continue;
}
*point++ = src[i];
}
*point = '\0'; //Terminate string properly
printf("%s\n", dst);
free(dst);
}
How should I return an empty string from a function? I tried using lcp[i] = ' ' but it creates an error. Then I used lcp[i] = 0 and it returned an empty string. However, I do not know if it's right.
Also, is it necessary to use free(lcp) in the caller function? Since I could not free and return at the same time.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_LEN 50
char *find_LCP(char str1[], char str2[]);
char *find_LCP(char str1[], char str2[]){
char * lcp = malloc(MAX_LEN * sizeof(char));
int a = strlen(str1);
int b = strlen(str2);
int min = a < b ? a : b;
for(int i = 0; i < min; i++){
if(str1[i] == str2[i])
lcp[i] = str1[i];
else
lcp[i] = 0;
}
return lcp;
}
int main()
{
char str1[MAX_LEN], str2[MAX_LEN];
char * lcp;
printf("Enter first word > ");
scanf("%s", str1);
printf("Enter second word > ");
scanf("%s", str2);
lcp = find_LCP(str1, str2);
printf("\nLongest common prefix: '%s'\n", lcp);
free(lcp);
return 0;
}
An "empty" string is just a string with the first byte zero, so you can write:
s[0] = 0;
However, it is not clear what you are trying to do. The LCP of "foo" and "fob" is "fo", not the empty string.
You can also return as soon as you find the first non-matching character, no need to go until the end.
Further, you can simply pass the output string as a parameter and have lcp be an array. That way you avoid both malloc and free:
char lcp[MAX_LEN];
...
find_LCP(lcp, str1, str2);
If you want to empty a string without using a for loop then you can do
lcp[0] = 0
but for emptying a string it was right the way you did using a for loop.
There are plenty other ways of emptying the string word by word using for loop:
lcp[i] = '\0';
and it's the right way to make string empty as letter by letter you trying to do using for loop
But if you are not using some loops and simply empty a string then you can do this.
memset(buffer,0,strlen(buffer));
but this will only work for zeroing up to the first NULL character.
If the string is a static array, you can use:
memset(buffer,0,sizeof(buffer));
Your program has a bug: If you supply two identical strings, lcp[i] = 0; never executes which means that your function will return a string which is not NUL-terminated. This will cause undefined behvaior when you use that string in your printf in main.
The fix for this is easy, NUL-terminate the string after the loop:
int i;
for (i = 0; i < min; i++){
if(str1[i] == str2[i])
lcp[i] = str1[i];
else
break;
}
lcp[i] = 0;
As for the answer to the question, an empty string is one which has the NUL-terminator right at the start. We've already handled that as we've NUL-terminated the string outside the loop.
Also, is it necessary to use free(lcp) in the caller function?
In this case, it is not required as the allocated memory will get freed when the program exits, but I'd recommend keeping it because it is good practice.
As the comments say, you can use calloc instead of malloc which fills the allocated memory with zeros so you don't have to worry about NUL-terminating.
In the spirit of code golf. No need to calculate string lengths. Pick any string and iterate through it until the current character either null or differs from the corresponding character in the other string. Store the index, then copy appropriate number of bytes.
char *getlcp(const char *s1, const char *s2) {
int i = 0;
while (s1[i] == s2[i] && s1[i] != '\0') ++i;
char *lcp = calloc((i + 1), sizeof(*lcp));
memcpy(lcp, s1, i);
return lcp;
}
P.S. If you don't care about preserving one of input strings then you can simplify the code even further and just return the index (the position of the last character of the common prefix) from the function, then put '\0' at that index into one of the strings.
I m trying to do this little programm with defensive programming but its more than difficult for me to handle this avoiding the Loop-Goto as i know that as BAD programming. I had try with while and do...while loop but in one case i dont have problem. Problem begins when i m going to make another do...while for the second case ("Not insert space or click enter button"). I tried and nested do...while but here the results was more complicated.
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
int main()
{
int i;
int length;
char giventext [25];
Loop:
printf("String must have 25 chars lenght:\n");
gets(giventext);
length = strlen(giventext);
if (length > 25) {
printf("\nString has over %d chars.\nMust give a shorter string\n", length);
goto Loop;
}
/* Here i trying to not give space or nothing*/
if (length < 1) {
printf("You dont give anything as a string.\n");
goto Loop;
} else {
printf("Your string has %d\n",length);
printf("Letter in lower case are: \n");
for (i = 0; i < length; i++) {
if (islower(giventext[i])) {
printf("%c",giventext[i]);
}
}
}
return 0;
}
Note that your code is not defensive at all. You have no way to avoid a buffer overflow because,
you check for the length of the string after it has been input to your program so after the buffer overflow has already occurred and
you used gets() which doesn't check input length and thus is very prone to buffer overflow.
Use fgets() instead and just discard extra characters.
I think you need to understand that strlen() doesn't count the number of characters of input but instead the number of characters in a string.
If you want to ensure that there are less than N characters inserted then
int
readinput(char *const buffer, int maxlen)
{
int count;
int next;
fputc('>', stdout);
fputc(' ', stdout);
count = 0;
while ((next = fgetc(stdin)) && (next != EOF) && (next != '\n')) {
// We need space for the terminating '\0';
if (count == maxlen - 1) {
// Discard extra characters before returning
// read until EOF or '\n' is found
while ((next = fgetc(stdin)) && (next != EOF) && (next != '\n'))
;
return -1;
}
buffer[count++] = next;
}
buffer[count] = '\0';
return count;
}
int
main(void)
{
char string[8];
int result;
while ((result = readinput(string, (int) sizeof(string))) == -1) {
fprintf(stderr, "you cannot input more than `%d' characters\n",
(int) sizeof(string) - 1);
}
fprintf(stdout, "accepted `%s' (%d)\n", string, result);
}
Note that by using a function, the flow control of this program is clear and simple. That's precisely why goto is discouraged, not because it's an evil thing but instead because it can be misused like you did.
Try using functions that label logical steps that your program needs to execute:
char * user_input() - returns an input from the user as a pointer to a char (using something other than get()! For example, look at scanf)
bool validate_input(char * str_input) - takes the user input from the above function and performs checks, such as validate the length is between 1 and 25 characters.
str_to_lower(char * str_input) - if validate_input() returns true you can then call this function and pass it the user input. The body of this function can then print the user input back to console in lower case. You could use the standard library function tolower() here to lower case each character.
The body of your main function will then be much simpler and perform a logical series of steps that tackle your problem. This is the essence of defensive programming - modularising your problem into separate steps that are self contained and easily testable.
A possible structure for the main function could be:
char * user_input();
bool validate_input(char *);
void str_to_lower(char *);
int main()
{
char * str_input = user_input();
//continue to get input from the user until it satisfies the requirements of 'validate_input()'
while(!validate_input(str_input)) {
str_input = user_input();
}
//user input now satisfied 'validate_input' so lower case and print it
str_to_lower(str_input);
return 0;
}
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);
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.