ends_with() ?

A

Andreas Klimas

Hello,

does anybody know about a standardfunction or a simpler way
to get this result.

int ends_with(char *string, char *has);

answer whether string ends with substring has or not.


my first approach:

int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return
name_len>=has_len &&
!memcmp(name+name_len-has_len, has, has_len);
}

best wishes

Andreas Klimas
 
I

infobahn

Andreas said:
Hello,

does anybody know about a standardfunction or a simpler way
to get this result.

int ends_with(char *string, char *has);

answer whether string ends with substring has or not.

my first approach:

int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return
name_len>=has_len &&
!memcmp(name+name_len-has_len, has, has_len);
}

That doesn't look terrible, although I wouldn't do it quite
like that.

Ideally, you should pass in the lengths if you can. Recalculating
this stuff takes time, so if you have the information already you
should provide it.

But if you must do it without knowing in advance how long your
strings are, I'd do it much the same way you did:

#include <assert.h>
#include <string.h>

int ends_with(const char *name, const char *has)
{
int rc = 0; /* 0 means "no", 1 means "yes" */
size_t name_len;
size_t has_len;

assert(name != NULL);
assert(has != NULL);

name_len = strlen(name);
has_len = strlen(has);

if(name_len >= has_len)
{
name += name_len;
name -= has_len;
if(strcmp(name, has) == 0)
{
rc = 1;
}
}

return rc;
}

Alternatives using standard library functions are likely to be
clumsy. Consider, for example, strstr. You'd have to loop it
in case the substring occurred earlier in the string but not
at the end. And strrchr sounds like a good bet until you think
it through.

The pointer arithmetic idea you had was sound enough. Note my
uses of const, size_t, and assert. Also, I've simplified the
code so that each line just does one job rather than six. :)
 
J

Joe Wright

Andreas said:
Hello,

does anybody know about a standardfunction or a simpler way
to get this result.

int ends_with(char *string, char *has);

answer whether string ends with substring has or not.


my first approach:

int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return
name_len>=has_len &&
!memcmp(name+name_len-has_len, has, has_len);
}

best wishes

Andreas Klimas

int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return name + name_len - has_len == has;

There is no point to the actual memcmp(). If the addresses are
equal, the strings are equal.
 
C

Chris McDonald

Joe Wright said:
int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return name + name_len - has_len == has;
There is no point to the actual memcmp(). If the addresses are
equal, the strings are equal.


Keh?
Doesn't work for ends_with("hello", "lo"), as it should.
Of course memcmp() or strcmp() will be required.
 
A

Andreas Klimas

Joe said:
int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return name + name_len - has_len == has;

There is no point to the actual memcmp(). If the addresses are equal,
the strings are equal.

char *get_content_type(char *filename) {
if (ends_with(filename, ".jpg")) return "image/jpg";
if (ends_with(filename, ".html")) return "text/html";

return "text/plain";
}
 
C

Chris Croughton

int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return name + name_len - has_len == has;

There is no point to the actual memcmp(). If the addresses are
equal, the strings are equal.

The last sentence is true, but it isn't complete. The strings can be
equal even though the addresses are different:

char str[] = "123456789";
if (ends_with(str, "789"))
printf("OK\n");

will never print OK with your implementation because the address of a
variable array will never equal that of a string constant, even though
789 is obviously at the end of 123456789. Indeed, by your reasoning
there would never be any point in having strcmp or memcmp...

Chris C
 
B

bd

Joe said:
int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return name + name_len - has_len == has;

There is no point to the actual memcmp(). If the addresses are
equal, the strings are equal.

This is true, but the strings may be equal even though the addresses aren't.
 
L

Lawrence Kirby

Keh?
Doesn't work for ends_with("hello", "lo"), as it should.

It could because string literals are allowed to share the same storage
when their contents are consistent. In this case "hello" contains
the complete sequence of "lo" i.e. 'l', 'o', '\0' so the compiler
could merge the two, although it doesn't have to. That's why Chris chose
to compare against data stored in a declared array.
Of course memcmp() or strcmp() will be required.

True

Lawrence
 
?

=?ISO-8859-1?Q?=22Nils_O=2E_Sel=E5sdal=22?=

Andreas said:
Hello,

does anybody know about a standardfunction or a simpler way
to get this result.
Not really.

//Note: does not accept NULL pointers.
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = strstr(haystack,needle);
if(r != NULL)
return *r[name_len] == 0;

return 0;
}
 
?

=?ISO-8859-1?Q?Bj=F8rn_Augestad?=

Nils said:
Andreas said:
Hello,

does anybody know about a standardfunction or a simpler way
to get this result.

Not really.

//Note: does not accept NULL pointers.
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = strstr(haystack,needle);
if(r != NULL)
return *r[name_len] == 0; What's with the * ?

return 0;
}


Will not work if needle has more than one occurence in haystack.

Bjørn
 
A

Andreas Klimas

Nils said:
Not really.

//Note: does not accept NULL pointers.

I would rather document that NULL pointers
would be accepted.
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = strstr(haystack,needle);
if(r != NULL)
return *r[name_len] == 0;

return 0;
}

/* more compact way */
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = strstr(haystack,needle);

return r&&!r[name_len];
}

best wishes
Andreas Klimas
 
M

Michael Mair

Andreas said:
Nils said:
Not really.

//Note: does not accept NULL pointers.


I would rather document that NULL pointers
would be accepted.
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = strstr(haystack,needle);
if(r != NULL)
return *r[name_len] == 0;

return 0;
}


/* more compact way */
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = strstr(haystack,needle);

return r&&!r[name_len];
}

/* deal with "hellolo" and "lo" */
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = haystack, *last = NULL;

while (r = strstr(r,needle))
last = r++;

return last&&!last[name_len];
}


Cheers
Michael
 
J

Jonathan Burd

Andreas said:
Hello,

does anybody know about a standardfunction or a simpler way
to get this result.

int ends_with(char *string, char *has);

answer whether string ends with substring has or not.


my first approach:

int ends_with(char *name, char *has) {
int name_len = strlen(name);
int has_len = strlen(has);
return
name_len>=has_len &&
!memcmp(name+name_len-has_len, has, has_len);
}

best wishes

Andreas Klimas

Here's my first approach:

/* xstrendswith.c */
#include <assert.h>
#include <stddef.h>

int xstrendswith(const char *h, const char *n);

/*
@synopsis
int xstrendswith (const char *h, const char *n);
@description
Determines if the string pointed to by haystack ends with
the string pointed to by needle.
@param h pointer to the haystack string
@param n pointer to the needle string
@return
1 -- the string pointed to by h ends with
the string pointed to by n
0 -- the string pointed to by h does not end with
the string pointed to by n
@references
none
*/
int
xstrendswith(const char *h, const char *n)
{
const char *eos_h = h;
const char *eos_n = n;
size_t len_n;
size_t len_h;
int ret = 1;

/* bad ptr check */
assert(h != NULL);
assert(n != NULL);
if (!n || !h)
{
return 0;
}

/* get lengths */
while (*eos_h) ++eos_h;
while (*eos_n) ++eos_n;
len_n = eos_n - n;
len_h = eos_h - h;

/* test lengths */
if (len_n <= len_h)
{
while (eos_n >= n && eos_h >= h)
{
if (*eos_n != *eos_h)
{
ret = 0;
break;
}
--eos_n, --eos_h;
}
}
else
{
ret = 0;
}

return ret;
}

#ifdef TEST
#include <stdio.h>

#define ELEMS 5

int
main (void)
{
struct
{
const char *s1;
const char *s2;
} test[ELEMS] =
{
{"foo","foo"},
{"", ""},
{"foofoofoo", "foo"},
{"arghfoo", "foo"},
{"foo", "arghfoo"}
/* uncomment lines below to test for NULL -- also set ELEMS */
/* ,
{NULL, "foo"},
{"foo", NULL} */
};
int i = 0, ret;
for (i = 0; i < ELEMS; ++i)
{
ret = xstrendswith(test.s1, test.s2);
printf (
"haystack:%s; needle:%s; ret: %d\n", test.s1, test.s2, ret
);
}

return 0;
}

#endif /* no TEST */

If I have overlooked a test case, please let me know. This routine
*should* work.

Regards,
Jonathan.
 
C

Chris McDonald

Andreas Klimas said:
/* more compact way */
int ends_with(char *haystack, char *needle) {
int name_len = strlen(needle);
char *r = strstr(haystack,needle);
return r&&!r[name_len];
}

These examples using strstr() may be shorter/more compact than ones using
strlen() and strcmp() when we count the number of statements, but their
runtime complexity will invariably be much worse.
 
?

=?ISO-8859-1?Q?Bj=F8rn_Augestad?=

Jonathan Burd wrote:

[snip]
while (eos_n >= n && eos_h >= h)
{
if (*eos_n != *eos_h)
{
ret = 0;
break;
}
--eos_n, --eos_h;
This will sooner or later set either eos_n or eos_h (or both) to n-1 or
h-1, which is undefined behaviour, AFAIK. See C99 §6.5.6#8 for all the
gory details.

[snip]
If I have overlooked a test case, please let me know. This routine
*should* work.

How about "foo", "" ?

Bjørn
 
J

Jonathan Burd

Jonathan said:
Andreas Klimas wrote:

Here's my first approach:

/* xstrendswith.c */
#include <assert.h>
#include <stddef.h>

int xstrendswith(const char *h, const char *n);

should be
#if TEST
int xstrendswith(const char *h, const char *n);
#endif

Anyway...

<snip>

Regards,
Jonathan.
 
J

Jonathan Burd

Jonathan said:
Andreas Klimas wrote:

Here's my first approach:

/* xstrendswith.c */
#include <assert.h>
#include <stddef.h>

int xstrendswith(const char *h, const char *n);

should be
#ifdef TEST
int xstrendswith(const char *h, const char *n);
#endif

Anyway...

<snip>

Regards,
Jonathan.
 
M

Michael Mair

Bjørn Augestad said:
Jonathan Burd wrote:

[snip]
while (eos_n >= n && eos_h >= h)
{
if (*eos_n != *eos_h)
{
ret = 0;
break;
}
--eos_n, --eos_h;

This will sooner or later set either eos_n or eos_h (or both) to n-1 or
h-1, which is undefined behaviour, AFAIK. See C99 §6.5.6#8 for all the
gory details.

Apart from that, the test after the && is unnecessary.
As we already have calculated strlen(n), we can of course use it
to check only the last strlen(n) characters before the string
terminator in haystack.
[snip]
If I have overlooked a test case, please let me know. This routine
*should* work.


How about "foo", "" ?

Or just "foo","bar" :)


Cheers
Michael
 
J

Jonathan Burd

Michael said:
Bjørn Augestad wrote:

Thanks for this. I didn't know about this. :)
Apart from that, the test after the && is unnecessary.
As we already have calculated strlen(n), we can of course use it
to check only the last strlen(n) characters before the string
terminator in haystack.

Excellent! I love you guys. :)

Or just "foo","bar" :)


Cheers
Michael

Thanks a million.

Here's the revised code:

/* xstrendswith.c */
#include <assert.h>
#include <stddef.h>

/*
@synopsis
int xstrendswith (const char *h, const char *n);
@description
Determines if the string pointed to by h ends with
the string pointed to by n.
@param h pointer to the haystack string
@param n pointer to the needle string
@return
1 -- the string pointed to by h ends with
the string pointed to by n
0 -- the string pointed to by h does not end with
the string pointed to by n
@references
none
*/
int
xstrendswith(const char *h, const char *n)
{
const char *eos_h = h;
const char *eos_n = n;
size_t len_n;
size_t len_h;
int ret = 1;

/* bad ptr check */
assert(h != NULL);
assert(n != NULL);
if (!n || !h)
{
return 0;
}

/* get lengths - get eos's to point to 1 element after NUL */
while (*eos_h++) continue;
while (*eos_n++) continue;
len_n = eos_n - n - 1;
len_h = eos_h - h - 1;

/* test lengths */
if (len_n <= len_h)
{
do
{
--eos_n, --eos_h;
if (*eos_n != *eos_h)
{
ret = 0;
break;
}
} while (eos_n > n);
#if 0 /* alt */
while (--eos_h, --eos_n > n)
{
if (*eos_n != *eos_h)
{
ret = 0;
break;
}
}
#endif
}
else
{
ret = 0;
}

return ret;
}

#define TEST

#ifdef TEST
#include <stdio.h>
int xstrendswith(const char *h, const char *n);

#define ELEMS 8

int
main (void)
{
struct
{
const char *s1;
const char *s2;
} test[ELEMS] =
{
{"foo", "foo" },
{"", "" },
{"foofoofoo", "foo" },
{"arghfoo", "foo" },
{"foo", "arghfoo" },
{"", "foo" },
{"foo", "" },
{"foo", "bar" }
/* uncomment lines below to test for NULL -- also set ELEMS */
/* ,
{NULL, "foo" },
{"foo", NULL } */
};
int i = 0, ret;
for (i = 0; i < ELEMS; ++i)
{
ret = xstrendswith(test.s1, test.s2);
printf (
"haystack:%s; needle:%s; ret: %d\n", test.s1, test.s2, ret
);
}

return 0;
}

#endif /* no TEST */

Again this *should* work. Anyway, too tired to drag myself any further.
Zzzz.

Regards,
Jonathan.
 
C

CBFalconer

And here is mine - somewhat tortuous. endswith() alone is short.
I think I neglected to take care of the phrase being longer than
the searchee, which could cause UB.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Yes, I know strlcat and strlcpy are in the implementors
namespace. Change them if you must.
See <http://cbfalconer.home.att.net/download/strlcpy.zip>
for documentation and rationale.

The objective is to detect whether a given string terminates
another string. This is a fairly tortuous way of doing
"endswith", but is fairly clear.

By C.B.Falconer. Released to public domain
*/

/* ---------------------- */

/* reverse string in place. Return length */
static size_t revstring(char *string)
{
char *last, temp;
size_t lgh;

if ((lgh = strlen(string)) > 1) {
last = string + lgh; /* points to '\0' */
while (last-- > string) {
temp = *string; *string++ = *last; *last = temp;
}
}
return lgh;
} /* revstring */

/* ---------------------- */

static size_t strlcpy(char *dst, const char *src, size_t sz)
{
const char *start = src;

if (src && sz--) {
while ((*dst++ = *src))
if (sz--) src++;
else {
*(--dst) = '\0';
break;
}
}
if (src) {
while (*src++) continue;
return src - start - 1;
}
else if (sz) *dst = '\0';
return 0;
} /* strlcpy */

/* ---------------------- */

static size_t strlcat(char *dst, const char *src, size_t sz)
{
char *start = dst;

while (*dst++) /* assumes sz >= strlen(dst) */
if (sz) sz--; /* i.e. well formed string */
dst--;
return dst - start + strlcpy(dst, src, sz);
} /* strlcat */

/* ---------------------- */

/* does searchme end with the phrase phrase? */
/* illustrates the power of reversing things */
/* (if not the efficacy) */
static int endswith(char *phrase, char *searchme)
{
int result, lgh, i;

lgh = revstring(phrase); revstring(searchme);
result = 1;
for (i = 0; i < lgh; i++) /* strncmp if we had it */
if (phrase != searchme) {
result = 0; break;
}
revstring(phrase); revstring(searchme);
return result;
} /* endswith */

/* ---------------------- */

int main(int argc, char **argv)
{
char *searchme, *tmp;
size_t searchlgh;
int i;

if (argc < 3) puts("Usage: endswith phrase string");
else {
searchlgh = 1 + strlen(argv[2]);
searchme = malloc(searchlgh);
strlcpy(searchme, argv[2], searchlgh);
for (i = 3; i < argc; i++) {
/* append blank and argv to searchme */
searchlgh += (1 + strlen(argv));
if (!(tmp = realloc(searchme, searchlgh))) break;
else {
searchme = tmp;
strlcat(searchme, " ", searchlgh);
strlcat(searchme, argv, searchlgh);
}
}
printf("\"%s\" does ", searchme);
if (!endswith(argv[1], searchme)) printf("not ");
printf("end with \"%s\"\n", argv[1]);
}
return 0;
} /* main, endswith */
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,159
Messages
2,570,879
Members
47,416
Latest member
LionelQ387

Latest Threads

Top