2010-04-29 40 views
5

Tôi muốn ném ba ký tự cuối cùng từ tên tệp và nhận phần còn lại?cách xóa tiện ích mở rộng khỏi tên tệp?

tôi có mã này: ""

char* remove(char* mystr) { 

    char tmp[] = {0}; 
    unsigned int x; 

    for (x = 0; x < (strlen(mystr) - 3); x++) 
     tmp[x] = mystr[x]; 

    return tmp; 
} 
+0

Có phải C++ không? –

+4

Bạn biết rằng "phần mở rộng tệp" và "ba ký tự cuối cùng của tên tệp" là ** không ** cùng một điều? Một số tệp có nhiều hơn hoặc ít hơn 3 ký tự trong extenson của chúng: 'Foo.java',' foo.c' là các ví dụ hay. Một số thậm chí có nhiều dấu chấm trong (các) tiện ích mở rộng: 'foo.tar.gz'. Và những người khác sẽ có dấu chấm bên ngoài phần mở rộng: 'foo.bar.txt'. Tóm lại: đó là một nhiệm vụ không tầm thường. –

+4

Nền tảng nào? Nó không phải là tầm thường để làm điều này một cách chính xác trong C, và không có gì trong thư viện chuẩn (vì tên tập tin là một nền tảng bit cụ thể). Ví dụ, dấu gạch chéo ngược là một dấu phân cách đường dẫn trong Windows nhưng không phải trong * nix, do đó, đối với 'some \ path.to \ file', kết quả của" remove the extension "trên Windows là' ​​some \ path.to \ file' (vì không có phần mở rộng), nhưng trên * nix là 'some \ path' (vì phần mở rộng là' to \ file'). –

Trả lời

12

Hãy thử:

char *remove(char* mystr) { 
    char *retstr; 
    char *lastdot; 
    if (mystr == NULL) 
     return NULL; 
    if ((retstr = malloc (strlen (mystr) + 1)) == NULL) 
     return NULL; 
    strcpy (retstr, mystr); 
    lastdot = strrchr (retstr, '.'); 
    if (lastdot != NULL) 
     *lastdot = '\0'; 
    return retstr; 
} 

Bạn sẽ phải giải phóng chuỗi trả lại cho mình. Nó chỉ đơn giản là tìm thấy . cuối cùng trong chuỗi và thay thế nó bằng một ký tự terminator null. Nó sẽ xử lý các lỗi (đi qua NULL hoặc hết bộ nhớ) bằng cách trả về NULL.

Nó sẽ không làm việc với những thứ như /this.path/is_bad vì nó sẽ tìm ra . trong phần phi-file nhưng bạn có thể xử lý này bằng cách cũng làm một strrchr của /, hoặc bất cứ điều gì tách con đường của bạn, và đảm bảo vị trí của nó là NULL hoặc trước vị trí ..


Một giải pháp mục đích tổng quát hơn cho vấn đề này có thể là:

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

// remove_ext: removes the "extension" from a file spec. 
// mystr is the string to process. 
// dot is the extension separator. 
// sep is the path separator (0 means to ignore). 
// Returns an allocated string identical to the original but 
// with the extension removed. It must be freed when you're 
// finished with it. 
// If you pass in NULL or the new string can't be allocated, 
// it returns NULL. 

char *remove_ext (char* mystr, char dot, char sep) { 
    char *retstr, *lastdot, *lastsep; 

    // Error checks and allocate string. 

    if (mystr == NULL) 
     return NULL; 
    if ((retstr = malloc (strlen (mystr) + 1)) == NULL) 
     return NULL; 

    // Make a copy and find the relevant characters. 

    strcpy (retstr, mystr); 
    lastdot = strrchr (retstr, dot); 
    lastsep = (sep == 0) ? NULL : strrchr (retstr, sep); 

    // If it has an extension separator. 

    if (lastdot != NULL) { 
     // and it's before the extenstion separator. 

     if (lastsep != NULL) { 
      if (lastsep < lastdot) { 
       // then remove it. 

       *lastdot = '\0'; 
      } 
     } else { 
      // Has extension separator with no path separator. 

      *lastdot = '\0'; 
     } 
    } 

    // Return the modified string. 

    return retstr; 
} 

int main (int c, char *v[]) { 
    char *s; 
    printf ("[%s]\n", (s = remove_ext ("hello", '.', '/'))); free (s); 
    printf ("[%s]\n", (s = remove_ext ("hello.", '.', '/'))); free (s); 
    printf ("[%s]\n", (s = remove_ext ("hello.txt", '.', '/'))); free (s); 
    printf ("[%s]\n", (s = remove_ext ("hello.txt.txt", '.', '/'))); free (s); 
    printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', '/'))); free (s); 
    printf ("[%s]\n", (s = remove_ext ("/has.dot/in.path", '.', '/'))); free (s); 
    printf ("[%s]\n", (s = remove_ext ("/no.dot/in_path", '.', 0))); free (s); 

    return 0; 
} 

và điều này tạo ra:

[hello] 
[hello] 
[hello] 
[hello.txt] 
[/no.dot/in_path] 
[/has.dot/in] 
[/no] 
+0

Tuyệt vời, công việc tuyệt vời. cảm ơn. – alaamh

+0

nếu tôi chỉ muốn tên tệp mà không có phần mở rộng và với đường dẫn? cảm ơn – alaamh

+0

bạn bị rò rỉ bộ nhớ trong cả hai trường hợp, bạn cần phải trả tự do. –

9

Sử dụng rindex để xác định vị trí tính cách. Nếu chuỗi có thể ghi được, bạn có thể thay thế bằng chuỗi terminator char ('\ 0') và bạn đã hoàn tất.

char * rindex (const char * s, int c);

DESCRIPTION 
The rindex() function locates the last character matching c (converted to a char) in the null-terminated string s. 
+3

Bản đồ này "some/path.name/readme" thành "some/path", đó là sai. Nó có thể là đầu vào này là không thể, nhưng tôi nghĩ rằng nó có giá trị nêu rõ các giả định. –

+1

Ngoài ra, bạn có thể cân nhắc việc thay đổi tên hàm. Thường trình remove() thường được sử dụng để xóa/xóa tệp khỏi thư mục. – Sparky

+4

Cần lưu ý rằng rindex không phải là ISO-C: strrchr có thể là một lựa chọn tốt hơn. – paxdiablo

3

tôi sẽ cố gắng thuật toán sau đây:

last_dot = -1 

for each char in str: 
    if char = '.': 
     last_dot = index(char) 

if last_dot != -1: 
    str[last_dot] = '\0' 
0

Chỉ cần thay thế dấu chấm bằng "0". Nếu bạn biết rằng tiện ích mở rộng của mình luôn dài 3 ký tự, bạn chỉ có thể thực hiện:

 
char file[] = "test.png"; 
file[strlen(file) - 4] = 0; 
puts(file); 

Điều này sẽ xuất "kiểm tra". Ngoài ra, bạn không nên trả về một con trỏ đến một biến cục bộ. Trình biên dịch cũng sẽ cảnh báo bạn về điều này.

0

này nên thực hiện công việc:

char* remove(char* oldstr) { 
    int oldlen = 0; 
    while(oldstr[oldlen] != NULL){ 
     ++oldlen; 
    } 
    int newlen = oldlen - 1; 
    while(newlen > 0 && mystr[newlen] != '.'){ 
     --newlen; 
    } 
    if (newlen == 0) { 
     newlen = oldlen; 
    } 
    char* newstr = new char[newlen]; 
    for (int i = 0; i < newlen; ++i){ 
     newstr[i] = oldstr[i]; 
    } 
    return newstr; 
} 
6

Nếu bạn có nghĩa là chỉ muốn loại bỏ ba nhân vật cuối cùng, bởi vì bạn bằng cách nào đó biết rằng tên tập tin của bạn có phần mở rộng đúng ba ký tự dài (và bạn muốn giữ lại dot):

char *remove_three(const char *filename) { 
    size_t len = strlen(filename); 
    char *newfilename = malloc(len-2); 
    if (!newfilename) /* handle error */; 
    memcpy(newfilename, filename, len-3); 
    newfilename[len - 3] = 0; 
    return newfilename; 
} 

Hoặc để cho người gọi cung cấp bộ đệm đích (mà họ phải đảm bảo đủ dài):

char *remove_three(char *dst, const char *filename) { 
    size_t len = strlen(filename); 
    memcpy(dst, filename, len-3); 
    dst[len - 3] = 0; 
    return dst; 
} 

Nếu bạn muốn loại bỏ một phần mở rộng tệp thông thường, điều đó khó hơn và thường sử dụng bất kỳ thường trình xử lý tên tệp nào mà nền tảng của bạn cung cấp (basename trên POSIX, _wsplitpath_s trên Windows) nếu có bất kỳ cơ hội nào bạn đang xử lý đường dẫn hơn chỉ là phần cuối cùng của filename:

/* warning: may modify filename. To avoid this, take a copy first 
    dst may need to be longer than filename, for example currently 
    "file.txt" -> "./file.txt". For this reason it would be safer to 
    pass in a length with dst, and/or allow dst to be NULL in which 
    case return the length required */ 
void remove_extn(char *dst, char *filename) { 
    strcpy(dst, dirname(filename)); 
    size_t len = strlen(dst); 

    dst[len] = '/'; 
    dst += len+1; 

    strcpy(dst, basename(filename)); 
    char *dot = strrchr(dst, '.'); 
    /* retain the '.' To remove it do dot[0] = 0 */ 
    if (dot) dot[1] = 0; 
} 

Hãy đến với suy nghĩ của nó, bạn có thể muốn vượt qua dst+1 hơn dst để strrchr, vì một tên tập tin bắt đầu với một dấu chấm có lẽ không nên được rút ngắn xuống còn " ". Phụ thuộc vào nó.

+0

+1 - Tốt hơn nhiều so với câu trả lời của tôi. –

1

Để có được thứ hai giải pháp mục đích tổng quát hơn paxdiablo để làm việc trong một C++ Tôi đã thay đổi dòng này:

if ((retstr = malloc (strlen (mystr) + 1)) == NULL) 

tới:

if ((retstr = static_cast<char*>(malloc (strlen (mystr) + 1))) == NULL) 

Hy vọng điều này sẽ giúp ai đó.

0

Nhận vị trí và chỉ sao chép tới vị trí đó vào một thẻ * mới.

i = 0; 
    n = 0; 
    while(argv[1][i] != '\0') { // get length of filename 
     i++; } 

    for(ii = 0; i > -1; i--) { // look for extension working backwards 
     if(argv[1][i] == '.') { 
      n = i; // char # of exension 
      break; } } 

memcpy(new_filename, argv[1], n); 
0

Đây là cách đơn giản để thay đổi tên tiện ích mở rộng.

.... 
char outputname[255] 
sscanf(inputname,"%[^.]",outputname); // foo.bar => foo 
sprintf(outputname,"%s.txt",outputname) // foo.txt <= foo 
.... 
+1

Điều này bị hỏng cho các đường dẫn tệp tương đối bắt đầu bằng '..' và có khả năng' .' – bazz

0

Với độ dài tệp tối thiểu có thể định cấu hình và độ dài mở rộng tối đa có thể định cấu hình. Trả về chỉ mục mà phần mở rộng đã được thay đổi thành ký tự null hoặc -1 nếu không tìm thấy phần mở rộng.

int32_t strip_extension(char *in_str) 
{ 
    static const uint8_t name_min_len = 1; 
    static const uint8_t max_ext_len = 4; 

    /* Check chars starting at end of string to find last '.' */ 
    for (ssize_t i = sizeof(in_str); i > (name_min_len + max_ext_len); i--) 
    { 
     if (in_str[i] == '.') 
     { 
      in_str[i] = '\0'; 
      return i; 
     } 
    } 
    return -1; 
} 
0

tôi sử dụng mã này:

void remove_extension(char* s) { 
    char* dot = 0; 
    while (*s) { 
    if (*s == '.') dot = s; // last dot 
    else if (*s == '/' || *s == '\\') dot = 0; // ignore dots before path separators 
    s++; 
    } 
    if (dot) *dot = '\0'; 
} 

Nó xử lý các ước con đường của Windows một cách chính xác (cả /\ có thể tách đường dẫn).

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