2010-04-29 160 views
5

我想从文件名抛出最后三个字符,并得到其余的?如何从文件名中删除扩展名?

我有这样的代码: “”

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++是否正常? – 2010-04-29 11:14:28

+4

你知道“文件扩展名”和“文件名的最后三个字符”是**不是**是同一个东西吗?有些文件的扩展名多于或少于3个字符:'Foo.java','foo.c'就是很好的例子。有些甚至在扩展中有多个点:'foo.tar.gz'。还有一些人在扩展名之外有一个点:'foo.bar.txt'。简而言之:这是一项不平凡的任务。 – 2010-04-29 11:15:24

+4

什么平台?在C中正确执行此操作并不重要,并且标准库中没有任何内容(因为文件名有点特定于平台)。例如,反斜杠是Windows中的路径分隔符,但不是* nix,所以对于'some \ path.to \ file',Windows上的“删除扩展名”的结果是'some \ path.to \ file'(因为没有扩展名),但是* nix是'some \ path'(因为扩展名是'to \ file')。 – 2010-04-29 11:16:39

回答

12

尝试:

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; 
} 

你必须自己释放返回的字符串。它只是在字符串中找到最后一个.并用空终止符替换它。它将通过返回NULL来处理错误(传递NULL或耗尽内存)。

,不会的东西像/this.path/is_bad工作,因为它会发现,在非文件部分的.,但是你可以通过也做的/一个strrchr,或者其他路径分隔符为解决这个问题,并确保它的位置是NULL或位于.之前。


一个更通用的解决这个问题可能是:

#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; 
} 

并且这产生:

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

优秀的,伟大的工作。谢谢。 – alaamh 2010-04-29 19:18:20

+0

如果我只想要没有扩展名和路径的文件名? 谢谢 – alaamh 2010-04-29 19:43:30

+0

你在这两种情况下都有内存泄漏,你需要释放retstr。 – 2016-02-03 01:13:42

9

使用RINDEX定位字符。如果字符串是可写的,则可以用字符串终止符char('\ 0')替换它,然后就完成了。

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

这将“some/path.name/readme”映射为“some/path”,这是错误的。可能这种投入是不可能的,但我认为值得说明这些假设。 – 2010-04-29 11:24:57

+1

此外,您可能希望考虑更改函数名称。例程remove()通常用于从目录中删除/删除文件。 – Sparky 2010-04-29 11:29:09

+4

值得注意的是rindex不是ISO-C:strrchr可能是更好的选择。 – paxdiablo 2010-04-29 12:21:29

3

我会尝试下面的算法:

last_dot = -1 

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

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

就用 “0” 代替点。如果你知道你的分机总是3个字符长,你可以这样做:

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

这将输出“测试”。另外,你不应该返回一个指向局部变量的指针。编译器也会对此提出警告。

0

这应该做的工作:

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

如果你真的只是想删除最后三个字符,因为你不知何故知道你的文件名有一个扩展恰好有三个字符长(你想保持点):

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; 
} 

或者让呼叫者提供目标缓冲区(他们必须保证足够长):

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

如果您想一般删除文件扩展名,这很难,并且通常应该使用您的平台提供的任何文件名处理例程(POSIX上的basename,Windows上的_wsplitpath_s),如果有任何机会处理路径不仅仅是文件名的最后部分:

/* 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; 
} 

试想想起来了,你可能想通过dst+1,而不是dst到strrchr,因为文件名以点开头,也许不应该被截断只是“ “。取决于它的用途。

+0

+1 - 比我的回答好得多。 – 2010-04-29 11:46:31

1

要获得paxdiablo的第二更通用的解决方案,在C工作++编译器我改变了这一行:

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

到:

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

希望这可以帮助别人。

0

获取位置并将其复制到新的char *位置。

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

这是更改扩展名的简单方法。

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

对于以“..”开头并且可能是“。”开头的相对文件路径,这被打破了。 – bazz 2015-09-02 15:05:54

0

具有可配置的最小文件长度和可配置的最大扩展长度。返回扩展名更改为空字符的索引,如果未找到扩展名,则返回-1。

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

我用这个代码:

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'; 
} 

它能够正确处理Windows路径约定(包括/\可以路径分隔符)。