【问题标题】:How do I strip a file extension from a string in C?如何从 C 中的字符串中删除文件扩展名?
【发布时间】:2017-08-27 01:59:21
【问题描述】:

如何从 C 中的字符串中去除文件扩展名?

例如,

如果我有带有文件扩展名的文件名

你好.txt

你好.c

你好.java

应该只产生不带扩展名的文件名

你好

文件扩展名的长度无关紧要。

该功能还应该是可移植的并且依赖于系统。

我知道如何使用strchrstrrchr 获取文件扩展名。我已经在 Bash 和 Python 中做到了这一点,但我从来没有在 C 中这样做过。

注意:这是一个个人练习,以帮助加强我对 C 编程语言的理解。

【问题讨论】:

  • 如果您要否决这篇文章,您至少可以说出原因。我认为这个网站应该提倡通过提出合法的有针对性的问题来学习。根据帮助中心的说法,我可以问一个“软件开发特有的实用、可回答的问题”的问题。根据我应该避免问的问题,“如果您的动机是“我希望其他人向我解释_”,那么您可能没问题。”。如果您觉得问题措辞不当,请对其进行编辑,使其更适合主题问题。

标签: c string replace split


【解决方案1】:
const char * s_name // pointing to ex "myString.ext"

char * s_atext = strstr(s_name, ".ext");
if (s_atext != NULL) {
    /* Extract the filename without extension in case only */
    size_t name_only_size = s_atext - s_name;
    char * s_name_only = strndup(s_name, name_only_size); 
    // use your s_name_only
    printf("FileName w/o ext: %s\n", s_name_only);
    if (s_name_only) free(s_name_only);
}

【讨论】:

【解决方案2】:

我不知道有没有最好的方法,但这里有一种方法。大概您不想从点文件中删除名称。此外,您可能只想删除从最后一个点到字符串末尾的字符。注意,在下面的代码中,输入的字符串被修改了,所以文件名不能是字符串字面量,而必须是一个以null结尾的字符数组。

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

void strip_ext(char *);

int main(void)
{
    char filename1[] = "myfile.txt";
    char filename2[] = ".emacs";
    char filename3[] = "anotherfile.";
    char filename4[] = "nodot";
    char filename5[] = "";
    char filename6[] = "dot.dot.dot";

    strip_ext(filename1);
    strip_ext(filename2);
    strip_ext(filename3);
    strip_ext(filename4);
    strip_ext(filename5);
    strip_ext(filename6);

    printf("file1: %s\n", filename1);
    printf("file2: %s\n", filename2);
    printf("file3: %s\n", filename3);
    printf("file4: %s\n", filename4);
    printf("file5: %s\n", filename5);
    printf("file6: %s\n", filename6);

    return 0;
}

void strip_ext(char *fname)
{
    char *end = fname + strlen(fname);

    while (end > fname && *end != '.') {
        --end;
    }

    if (end > fname) {
        *end = '\0';
    }
}

程序输出:

file1: myfile
file2: .emacs
file3: anotherfile
file4: nodot
file5: 
file6: dot.dot

更新

@David C. Rankin 指出更复杂的文件路径可能会使事情复杂化。下面是对strip_ext() 函数的修改,该函数在遇到正斜杠或反斜杠时停止查找点,在这种情况下,文件名字符串保持不变:

void strip_ext(char *fname)
{
    char *end = fname + strlen(fname);

    while (end > fname && *end != '.' && *end != '\\' && *end != '/') {
        --end;
    }
    if ((end > fname && *end == '.') &&
        (*(end - 1) != '\\' && *(end - 1) != '/')) {
        *end = '\0';
    }  
}

使用相同的测试字符串和附加的测试字符串运行此函数:

char filename7[] = "/my.dir/myfile";
char filename8[] = "/dir/.filename";

产生这个输出:

file1: myfile
file2: .emacs
file3: anotherfile
file4: nodot
file5: 
file6: dot.dot
file7: /my.dir/myfile
file8: /dir/.filename

【讨论】:

  • 为什么end &gt; fname 用作条件表达式?它们不只是指向字符串的第一个字符吗?
  • endfname 是指向char 的指针,它们指向同一个数组对象。 end\0 开始,fname 当然指向文件名的第一个字符。 If the objects pointed to are members of the same aggregate object... pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values..
  • 所以,如果我理解正确,你告诉end 指向数组中的最后一个元素。然后将其与fname 进行比较,结果为真。一旦对象相等,它就会产生错误,本质上是“剥离”'.' 字符之后的任何内容,然后将'.' 替换为空字符。
  • 我不得不说,这非常聪明,而且非常微妙。大声笑,C,你又得到了我。所以,似乎无论我做什么,我都必须用一个空字符替换'.' 字符,这只是终止那里的字符串,其余的留在内存中。非常酷,无论哪种方式。谢谢你的令人敬畏的回答。
  • 我喜欢开头,但是/my.dir/myfile 会发生什么?
【解决方案3】:

这可能不是最好的方法,但可能的解决方案是在点的位置放置一个 NULL 终止符,可以通过以下代码实现。

char *filename_dot = strchr(filename, '.');
int offset = filename_dot - filename;
filename[offset] = '\0';

【讨论】:

    【解决方案4】:

    有很多方法可以做到这一点。一种方法是将第一个点替换为空字符 (\0)。

    char [] filename = "Hello.java";
    
    for(int i=0;i<strlen(filename);i++) {
       if(filename[i] == '.') {
           filename[i]='\0';
           break;
       }
    }
    

    【讨论】:

    • 为了防止重复调用strlen来测试循环退出条件,最好是int len = (int)strlen (filename); for (int i = 0; i &lt; len; i++) {...}
    • @DavidC.Rankin,绝对。然而,我在这里的目的是向 OP 展示一个可行的答案,而不一定是一个面向性能的成熟解决方案。但我始终感谢您的反馈。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 1970-01-01
    • 2011-04-09
    • 2015-08-25
    • 2012-06-29
    • 2023-03-10
    相关资源
    最近更新 更多