2009-04-13 24 views
29

Tôi muốn đảm bảo chuỗi của mình kết thúc bằng ".foo". Tôi đang sử dụng C, một ngôn ngữ mà tôi không hoàn toàn quen thuộc. Cách tốt nhất tôi đã tìm thấy để làm điều đó là dưới đây. Bất kỳ C gurus muốn chắc chắn rằng tôi đang làm điều này một cách tao nhã và khôn ngoan?Làm thế nào để so sánh các đầu của chuỗi trong C?

int EndsWithFoo(char *str) 
{ 
    if(strlen(str) >= strlen(".foo")) 
    { 
     if(!strcmp(str + strlen(str) - strlen(".foo"), ".foo")) 
     { 
      return 1; 
     } 
    } 
    return 0; 
} 

Trả lời

2

Không yêu cầu strlen(".foo") s. Nếu bạn thực sự muốn nó linh hoạt, bạn có thể sử dụng sizeof ".foo" - 1 - hằng số thời gian biên dịch.

Ngoài ra, kiểm tra chuỗi rỗng sẽ tốt.

+0

Đúng nếu tôi sai, nhưng không phải là sizeof (". Foo") 5, nhưng strlen (". Foo") 4? Tôi nghĩ strlen dễ đọc hơn, vì tôi đang xử lý chuỗi dài ở đây. Và trình biên dịch nên tối ưu hóa nó với một hằng số ... Phần còn lại của chức năng trông như thế nào? –

+0

Cụ thể, không cần thiết vì chúng tôi đã biết ".foo" trong bao lâu. – Chuck

+0

Không phải là ".foo" một const char *? Ngay cả khi được tính là một mảng, nó có năm ký tự, vì ở dạng mảng nó có kết thúc '\ 0'. –

7

Tôi không có quyền truy cập vào trình biên dịch ngay bây giờ, vì vậy ai đó có thể cho tôi biết điều này có hiệu quả không?

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

int EndsWithFoo(const char* s); 

int 
main(void) 
{ 
    printf("%d\n", EndsWithFoo("whatever.foo")); 

    return 0; 
} 

int EndsWithFoo(const char* s) 
{ 
    int ret = 0; 

    if (s != NULL) 
    { 
    size_t size = strlen(s); 

    if (size >= 4 && 
     s[size-4] == '.' && 
     s[size-3] == 'f' && 
     s[size-2] == 'o' && 
     s[size-1] == 'o') 
    { 
     ret = 1; 
    } 
    } 

    return ret; 
} 

Dù sao, hãy đảm bảo đủ điều kiện tham số là const, nó cho mọi người biết (bao gồm trình biên dịch) mà bạn không có ý định sửa đổi chuỗi.

+0

+1 được tối ưu hóa nhất. Tôi thích một phiên bản như vậy khi 'foo' không thay đổi! – dirkgently

+3

Chỉ cần một mẹo: Nếu bạn có một kết nối internet, bạn có một trình biên dịch C có sẵn tại codepad.org –

+1

Luật Murphy nói rằng ".foo" sẽ thay đổi và tại thời điểm không thích hợp nhất. – plinth

43

Đừng gọi strlen nhiều lần cho mỗi chuỗi.

int EndsWith(const char *str, const char *suffix) 
{ 
    if (!str || !suffix) 
     return 0; 
    size_t lenstr = strlen(str); 
    size_t lensuffix = strlen(suffix); 
    if (lensuffix > lenstr) 
     return 0; 
    return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; 
} 

int EndsWithFoo(const char *str) { return EndsWith(str, ".foo"); } 

CHỈNH SỬA: thêm NULL kiểm tra pedantic. Đối với siêu pedantic, tranh luận liệu nó có nên trả về khác không nếu cả str và hậu tố đều là NULL.

+0

Trong trường hợp này, bạn có thể sử dụng strcmp() thay vì strncmp() (hoặc thậm chí memcmp()), vì chúng ta biết chính xác có bao nhiêu ký tự còn lại trong cả hai chuỗi tại thời điểm đó, đáng chú ý. –

+1

mọi cuộc gọi tới strlen biến mất khỏi assembly ngay khi bạn bật tối ưu hóa, vì vậy đó có thể là trường hợp tối ưu hóa sớm (mặc dù chuỗi C đủ mạnh để làm cho một suy nghĩ về các vấn đề đó) – Joey

+1

@Johannes: Làm thế nào điều này có thể, cho các chuỗi khác với các chuỗi ký tự được biên dịch theo thời gian? Chắc chắn bạn có thể nội tuyến mã strlen, nhưng ở một mức độ nào đó bạn vẫn cần phải tìm độ dài của chuỗi. Đối với các chuỗi ký tự const, trình biên dịch biết nó kéo dài bao lâu, nhưng điều này không đúng. Suy nghĩ? –

-3

Tôi muốn đề xuất cách tốt nhất để làm là đảo ngược chuỗi rồi so sánh các ký tự đầu tiên.

Có một số ví dụ về các chức năng đảo ngược chuỗi (thậm chí Joel trích dẫn nó như một câu hỏi phỏng vấn chuẩn) vì vậy chỉ cần thực hiện một trong số đó sau đó thực hiện các chuỗi đảo ngược để so sánh.

CHỈNH SỬA để phản hồi các downvotes. OK có, cách tiếp cận này yêu cầu CPU hoặc bộ nhớ bổ sung để thực hiện, nhưng người hỏi không chỉ ra bất kỳ ràng buộc nào như vậy và anh ta đã yêu cầu một giải pháp thanh lịch một cách rõ ràng. Đảo ngược các chuỗi sau đó so sánh từ phía trước là xa hơn thanh lịch hơn rối tung xung quanh việc tìm kiếm kết thúc của chuỗi và làm việc ngược. Và nó dễ dàng hơn rất nhiều cho lập trình viên tiếp theo để nắm bắt và duy trì quá.

+0

Có, điều đó sẽ hoạt động, nhưng đó là hoặc cấp phát bộ nhớ cho bản sao hoặc đảo ngược đôi để hoàn tác thiệt hại. – plinth

+0

Vì vậy, những gì? Không có dấu hiệu cho thấy anh ta đang hoạt động dưới sự điều khiển của CPU hoặc bộ nhớ và đảo ngược chuỗi so sánh từ phía trước là dễ bảo trì hơn so với việc tìm kiếm vị trí cuối cùng và hack logic từ đó. – Cruachan

+0

Bạn sẽ phải tìm phần cuối của chuỗi để biết nó đã mất bao lâu để bạn biết cách đảo ngược nó! Nó không quan trọng như thế nào bạn đi về nó, vấn đề liên quan đến sự kết thúc của chuỗi, vì vậy bạn sẽ phải tìm nó theo cách này hay cách khác. – dreamlax

0

Bạn cũng có thể khái quát như thế này:

int endsWith(const char* text, const char* extn) 
{ 
    int result = 1; 
    int len = strlen(text); 
    int exprLen = strlen(extn); 
    int index = len-exprLen; 
    int count = 0; 

    if(len > exprLen) 
    { 
     for(; count < exprLen; ++count) 
     { 
      if(text[index + count] != extn[count]) 
      { 
       result = 0; 
       break; 
      } 

     } 
    } 
    else 
    { 
     result = 0; 
    } 
    return result; 
} 
+0

Tôi quên có một strncmp ... – Naveen

2

đang Tested, bao gồm các bài kiểm tra:

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

int ends_with_foo(const char *str) 
{ 
    char *dot = strrchr(str, '.'); 

    if (NULL == dot) return 0; 
    return strcmp(dot, ".foo") == 0; 
} 

int main (int argc, const char * argv[]) 
{ 
    char *test[] = { "something", "anotherthing.foo" }; 
    int i; 

    for (i = 0; i < sizeof(test)/sizeof(char *); i++) { 
     printf("'%s' ends %sin '.foo'\n", 
       test[i], 
       ends_with_foo(test[i]) ? "" : "not "); 
    } 
    return 0; 
} 
+0

Chụp! Mặc dù tôi không bận tâm so sánh kết quả strcmp() - chỉ cần trả lại trực tiếp. –

+0

bạn giả định rằng không có '.' Khác trong chuỗi đầu vào. – Naveen

+0

Nó không tạo sự khác biệt. Strcmp() sẽ chỉ trả về 0 nếu có hai chuỗi giống hệt nhau (ví dụ: độ dài phải giống nhau). Việc so sánh sẽ kết thúc sớm nếu các chuỗi có độ dài khác nhau. –

8
int EndsWithFoo(char *string) 
{ 
    string = strrchr(string, '.'); 

    if(string != NULL) 
    return(strcmp(string, ".foo")); 

    return(-1); 
} 

Sẽ trở lại 0 nếu kết thúc bằng ".foo".

+0

Đẹp. Ngắn và dễ đọc. – EvilTeach

+0

* cung khiêm tốn * bạn rất tử tế :-) –

+4

Giá trị trả lại phải được đảo ngược, vì số không là false và khác 0, và tên hàm cho biết trả về Boolean. – dreamlax

0

Có lẽ ...

bool endswith (const char* str, const char* tail) 
{ 
    const char* foo = strrstr (str, tail); 
    if (foo) 
    { 
    const int strlength = strlen (str); 
    const int taillength = strlen (tail); 
    return foo == (str + strlength - taillength); 
    } 
    return false; 
} 

endswith (str, ".foo"); 

Bằng cách này, các giải pháp trong câu hỏi ban đầu có vẻ tốt đẹp khác so với các cuộc gọi strlen lặp đi lặp lại.

+0

strrstr() không chuẩn và không tồn tại, ít nhất là trên glibc 2.15 – lumpidu

0

Nếu luôn có một cái gì đó vượt ra ngoài dấu chấm, chúng ta có thể thưởng thức trong một số con trỏ số học:

int EndsWithFoo (char *str) 
{ 
    int iRetVal = 0; 
    char * pchDot = strrchr (str, '.'); 

    if (pchDot) 
    { 
     if (strcmp (pchDot+1, "foo") == 0) 
     { 
     iRetVal = 1; 
     } 
    } 
    return iRetVal; 
} 

Tất nhiên bạn có thể sẽ muốn thêm một chút strlen đó để kiểm tra có một cái gì đó vượt ra ngoài chấm: -)

NB - Tôi không chạy nó để kiểm tra, nhưng có vẻ ổn với tôi.

3

Nếu bạn có thể thay đổi chữ ký của chức năng của bạn, sau đó hãy thử thay đổi nó để

int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf); 

Điều này sẽ dẫn đến mã an toàn hơn, tái sử dụng nhiều hơn và hiệu quả hơn:

  1. Các vòng loại const thêm sẽ đảm bảo bạn không nhầm lẫn thay đổi chuỗi đầu vào. Hàm này là một biến vị ngữ, vì vậy tôi cho rằng nó không bao giờ có nghĩa là có các tác dụng phụ.
  2. Hậu tố so sánh ngược lại được chuyển thành tham số, vì vậy bạn có thể lưu hàm này để sử dụng lại sau này với các hậu tố khác.
  3. Chữ ký này sẽ cho bạn cơ hội để vượt qua độ dài của các chuỗi nếu bạn đã biết chúng. Chúng tôi gọi đây là dynamic programming.

Chúng ta có thể xác định các chức năng như vậy:

int EndsWith(char const * str, char const * suffix, int lenstr, int lensuf) 
{ 
    if(! str && ! suffix) return 1; 
    if(! str || ! suffix) return 0; 
    if(lenstr < 0) lenstr = strlen(str); 
    if(lensuf < 0) lensuf = strlen(suffix); 
    return strcmp(str + lenstr - lensuf, suffix) == 0; 
} 

Rõ ràng phản đối số cho các thông số thêm là họ bao hàm nhiều tiếng ồn trong các mã, hoặc một mã số ít biểu cảm.

-1

tôi sẽ làm điều đó như thế này:

/** 
    * Return 0 if the string haystack ends with the string needle 
    * 
    * @param haystack the string to be analyzed 
    * @param needle the suffix string 
    * @return 0 if the string haystack ends with the string needle, 1 if not 
*/ 
int strbcmp(const char *haystack, const char *needle) { 
    int length; 
    if (haystack && needle && strlen(haystack) >= (length = strlen(needle)) && strlen(strstr(haystack, needle)) == length) return 0; 
    return 1; 
} 

Các chương trình thử nghiệm là:

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

int strbcmp(const char *haystack, const char *needle) { 
    int length; 
    if (haystack && needle && strlen(haystack) >= (length = strlen(needle)) && strlen(strstr(haystack,needle)) == length) return 0; 
    return 1; 
} 

int main (int argc, char * argv[]){ 
    char *a = "file1.gz"; 
    char *b = "1.gz"; 
    char *c = NULL; 
    char *d = "1.gzabc"; 

    printf("%s %s = %d\n",a,b,strbcmp(a,b)); 
    printf("%s %s = %d\n",a,c,strbcmp(a,c)); 
    printf("%s %s = %d\n",d,b,strbcmp(d,b)); 

    return 0; 
} 
+0

Thậm chí không biên dịch – lumpidu

+0

Xin lỗi vì lỗi chính tả trong mã nguồn. Nó là tốt bây giờ. –

+1

Bài kiểm tra sau không hoạt động với việc triển khai của bạn: char * e = "1.gzabc1.gz"; printf ("% s% s =% d \ n", e, b, strbcmp (e, b)); – lumpidu

0

Tôi muốn sử dụng phiên bản của tôi:

bool endsWith(const char *filename, const char *ext) { 
    const uint len = strlen(filename); 
    const uint extLen = strlen(ext); 
    if (len < extLen) { 
     return false; 
    } 
    for (uint index = 1; index <= extLen; index++) { 
     if (filename[len - index] != ext[extLen - index]) { 
      return false; 
     } 
    } 
    return true; 
} 
0

giải pháp chung với một strlen (kim), strstr() và thử nghiệm cho '\ 0':

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

bool endsWith(const char* haystack, const char* needle) 
{ 
    bool rv = false; 
    if (haystack && needle) 
    { 
     size_t needle_size = strlen(needle); 
     const char* act = haystack; 
     while (NULL != (act = strstr(act, needle))) 
     { 
      if (*(act + needle_size) == '\0') 
      { 
       rv = true; 
       break; 
      } 
      act += needle_size; 
     } 
    } 

    return rv; 
} 

int main (int argc, char * argv[]) 
{ 
    char *a = "file1.gz"; 
    char *b = "1.gz"; 
    char *c = NULL; 
    char *d = "1.gzabc"; 
    char *e = "1.gzabc1.gz"; 

    printf("endsWith:\n"); 
    printf("%s %s = %d\n",a,b,endsWith(a,b)); 
    printf("%s NULL = %d\n",a,endsWith(a,c)); 
    printf("%s %s = %d\n",d,b,endsWith(d,b)); 
    printf("%s %s = %d\n",e,b,endsWith(e,b)); 

    return 0; 
} 
+0

Khi downvoting, xin vui lòng cho một bình luận tại sao. Giải pháp của tôi ít nhất là ... – lumpidu

0

Xin lỗi tôi đã đến muộn bữa tiệc. Bạn không thể làm điều gì đó với một số phép toán con trỏ đơn giản?

char* str = "hello.foo"; //this would be string given 

int x = 4; //.foo has 4 characters 

int n = strlen(str)- x; //where x is equal to suffix length 

char* test = &str[n]; //do some pointer math to find the last characters 

if(strcmp(test, ".foo") == 0){ 
    //do some stuff 
}// end if 

Con trỏ ký tự hoạt động bằng cách trỏ đến ký tự đầu tiên trong mảng của chúng. Vì vậy, khi bạn làm điều này, bạn đặt ký tự đầu tiên của thử nghiệm là '.' trong '.foo' (nếu đó là những gì nó chứa). Đó cũng là lý do tại sao bạn không cần phải cấp phát bộ nhớ cho nó vì nó chỉ trỏ vào mảng ký tự đã tồn tại.

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

int string_has_suffix(const char *str, const char *suf) 
{ 
    assert(str && suf); 

    const char *a = str + strlen(str); 
    const char *b = suf + strlen(suf); 

    while (a != str && b != suf) { 
     if (*--a != *--b) break; 
    } 
    return b == suf && *a == *b; 
} 

// Test Unit 
int main (int argc, char *argv[]) 
{ 
    assert(string_has_suffix("", "")); 
    assert(!string_has_suffix("", "a")); 
    assert(string_has_suffix("a", "")); 
    assert(string_has_suffix("a", "a")); 
    assert(!string_has_suffix("a", "b")); 
    assert(!string_has_suffix("a", "ba")); 
    assert(string_has_suffix("abc", "abc")); 
    assert(!string_has_suffix("abc", "eeabc")); 
    assert(!string_has_suffix("abc", "xbc")); 
    assert(!string_has_suffix("abc", "axc")); 
    assert(!string_has_suffix("abcdef", "abcxef")); 
    assert(!string_has_suffix("abcdef", "abxxef")); 
    assert(string_has_suffix("b.a", "")); 
    assert(string_has_suffix("b.a", "a")); 
    assert(string_has_suffix("b.a", ".a")); 
    assert(string_has_suffix("b.a", "b.a")); 
    assert(!string_has_suffix("b.a", "x")); 
    assert(string_has_suffix("abc.foo.bar", "")); 
    assert(string_has_suffix("abc.foo.bar", "r")); 
    assert(string_has_suffix("abc.foo.bar", "ar")); 
    assert(string_has_suffix("abc.foo.bar", "bar")); 
    assert(!string_has_suffix("abc.foo.bar", "xar")); 
    assert(string_has_suffix("abc.foo.bar", ".bar")); 
    assert(string_has_suffix("abc.foo.bar", "foo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "xoo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "foo.ba")); 
    assert(string_has_suffix("abc.foo.bar", ".foo.bar")); 
    assert(string_has_suffix("abc.foo.bar", "c.foo.bar")); 
    assert(string_has_suffix("abc.foo.bar", "abc.foo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "xabc.foo.bar")); 
    assert(!string_has_suffix("abc.foo.bar", "ac.foo.bar")); 
    assert(string_has_suffix("abc.foo.foo", ".foo")); 
    assert(string_has_suffix("abc.foo.foo", ".foo.foo")); 
    assert(string_has_suffix("abcdefgh", "")); 
    assert(!string_has_suffix("abcdefgh", " ")); 
    assert(string_has_suffix("abcdefgh", "h")); 
    assert(string_has_suffix("abcdefgh", "gh")); 
    assert(string_has_suffix("abcdefgh", "fgh")); 
    assert(!string_has_suffix("abcdefgh", "agh")); 
    assert(string_has_suffix("abcdefgh", "abcdefgh")); 

    return 0; 
} 

// $ gcc -Wall string_has_suffix.c && ./a.out 
0

Tôi luôn kiểm tra các chức năng chuỗi glib, chúng có tất cả các loại bit hữu ích. Chức năng kiểm tra hậu tố đã tồn tại.

gchar * str; 

if (!g_str_has_suffix(str)) { 
    return FALSE; 
} 

Tôi mới ở C, vì vậy tôi xin lỗi nếu đây không phải là 100% ...nhưng nó trông giống như một mệnh đề bảo vệ vững chắc đối với tôi!

Các vấn đề liên quan