【问题标题】:escaped whitespaces in paths osx/unix/darwin, plain Cosx/unix/darwin 路径中的转义空格,纯 C
【发布时间】:2015-04-10 18:11:52
【问题描述】:

这是一个简单的文件路径纯 C 例程,它应该以编程方式扩展波浪线并接受空格作为文件名中的合法字符。它适用于以下名称:

~/测试文件夹/test.txt

/Users/Shared/测试文件夹/test.txt

但如果将文件的图标拖放到 Terminal.app 的窗口,则似乎不适用于 Apple 建议的路径语法或 Terminal.app 生成的路径语法:

/Users/Shared/Test\文件夹/test.txt

~/Test\文件夹/test.txt

这是代码。我显然错过了将“转义空格”替换为“纯空格”字符的例程。另一方面,任何搜索 '\' 的例程都会导致编译器抱怨“未知转义序列”0x20(顺便说一句,这似乎是某些 unix 和 linux 系统中的有效空白代码,在 OSX 中可能不是?)。

是否有解决纯 C 和 C 字符串问题的方法,而无需处理 Apple 专有的 CFStrings 和 NSStrings,我知道解决方案很简单?我只是负担不起使用这个程序中的任何一个。请不要使用 shell 脚本、ruby、gawk、grep、perl、python 等。

提前致谢!

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

void get_file_path(char path[])
{
    char *p;
    size_t len = 0;
    wordexp_t exp_result;

    printf("Enter file name: \n");    
    p = fgets(path, TEXT_SIZE, stdin);
    fflush(stdin);

    if(p != NULL)len = strlen(path);

    //get rid of newline characters
    for(p = path; p < path + len; p++){
        if(*p == '\n') *p = '\0';
    }

    if(path[0] == '~'){
        wordexp(path, &exp_result, 0);
        //printf("Num.expansions: %zd\n", exp_result.we_wordc);
        strcpy(path, exp_result.we_wordv[0]);        

        for(size_t i = 1; i < exp_result.we_wordc; i++){
        //printf("%s\n", exp_result.we_wordv[i]);
        strcat(path, " ");
        strcat(path, exp_result.we_wordv[i]);
        }
    wordfree(&exp_result);        
    }
    printf("File path: %s\n", path);
    return;
}

这是外壳输出:

Enter file name: 
/Users/Shared/Test\ Folder/test.txt
File path: /Users/Shared/Test\ Folder/test.txt
Can't open file: /Users/Shared/Test\ Folder/test.txt

【问题讨论】:

  • 您不需要将"\ " 替换为" ",因为这将由shell AFAIK 完成。
  • 如果是这样,任何提到的路径语法都可以工作。但既然它没有,它似乎 不是 由 shell 完成的,不管有没有原因。请随意尝试一下。我已经发布了代码。
  • 我没有 Mac,所以它的行为可能会有所不同。
  • Mac 使用 bash 作为默认 shell。它在那里的工作方式与在 Linux 上相同。
  • 你测试过这个例子吗?我知道它应该可以在 Linux 上运行?然而,我有点担心从理论上讨论 bash 作为默认 shell,应该在任一系统上都可以正常工作,这似乎有点离题了。

标签: c macos unix darwin


【解决方案1】:

iharob 是正确的,shell 会将 "\ " 的不带引号或双引号 ("...") 外观转换为单个空格字符。生成的空格不会拆分单词。另一方面,只有当您首先向外壳提供这样的字符串时才会发生这种情况。如果您绕过 shell,则不会发生这种情况,例如当您通过 exec() 系列函数直接启动进程时,或者当字符串仅在程序内部使用时。

您似乎很难区分 shell 语法和 C 语法。 shell 将反斜杠字符解释为 通用 转义字符,但 C 仅识别特定的转义序列(全部由反斜杠引入),'\ ' 不是其中之一。

另一方面,'\\' 其中之一:它表示单个反斜杠字符。因此,如果您正在寻找一个双字符字符串的字符串文字,其元素是反斜杠后跟一个空格,那么在 C 中拼写为 "\\ "

更新:

除此之外,我认为您真正的问题是您的程序仅在字符串的第一个字符是~ 时才执行扩展。在您提供的测试运行中情况并非如此。在这种情况下,如果无条件执行扩展,您的代码(或多或少)对我有用。

【讨论】:

  • 另请注意,您也需要转义文字反斜杠以将其呈现给 shell。通过将文件拖放到 Terminal.app 上生成的路径语法旨在供 shell 使用,这绝对适合该上下文。
  • 谢谢 - 你可能是对的。此路径参数与 shell 无关,因此我坚持使用纯 C,区分 C 和 bash 没有问题。我只知道使用我的程序的人将文件图标拖放到终端窗口,因为他们对复制长而复杂的文件路径的拼写错误感到沮丧 - 并抱怨这个问题。我所做的只是想办法帮助他们。我会按照您的建议试一试,并让社区知道它是如何运作的。
  • @user3078414,我已经更新了我的答案,解释了为什么您提供的代码没有执行您预期的扩展。同样,这一切都与 OS X 和 Linux 之间的差异无关。
  • 是的,您对第一个波浪字符限制是正确的,除了您可能认为我可能期望的。这个限制是有目的的。不过还是谢谢。
【解决方案2】:

这个稍微修改过的代码似乎可以完成这项工作。这是一个非常简单的解决方案,它将除文字反斜杠 (\) 之外的所有字符复制到辅助字符数组,并在搜索数组开头的波浪号 (~) 之前调整该数组的长度,并扩展路径以防找到波浪号.感谢大家的讨论、解释、建议和cmets!

bool get_file_path(char path[]){
char *p, *p1;
char path1[TEXT_SIZE];
size_t len = 0;
size_t len1, i;
wordexp_t exp_result;

printf("Enter file name: \n"); 
fix:
p = fgets(path, TEXT_SIZE, stdin); 
if(p == NULL) return false;   
else{
len = strlen(path);
if(strcmp(path,"\n") == 0)goto fix;
}
//get rid of "escaped whitespaces"
for(p = path, p1 = path1, len1 = len; p < path + len; p++, p1++){
    if(*p == '\\'){p++; len1--;}
    *p1 = *p;
}        
//get rid of newline characters
for(p1 = path1; p1 < path1 + len1; p1++){
    if(*p1 == '\n') *p1 = '\0';
}    
if(path1[0] == '~'){
    wordexp(path1, &exp_result, 0);
    //printf("Num.expansions: %zd\n", exp_result.we_wordc);
    strcpy(path1, exp_result.we_wordv[0]);        
    for(i = 1; i <  exp_result.we_wordc; i++){
    //printf("%s\n", exp_result.we_wordv[i]);
    strcat(path1, " ");
    strcat(path1, exp_result.we_wordv[i]);
    }
wordfree(&exp_result);        
}
strcpy(path, path1);    
printf("File path: %s\n", path);
return true;
}

更新:

由于 fflush(stdin) 会生成除 Linux 之外的未定义行为,因此三行额外的代码可以帮助摆脱 stdin 缓冲区中的“杂散换行符”,而不是依赖在 fflush() 上进行操作。

【讨论】:

    【解决方案3】:

    这不是代码主要问题的答案,而是代码的问题。

    if(path[0] == '~'){ 是一个微妙的问题。

    p = fgets(path, TEXT_SIZE, stdin);
    ...
    if(path[0] == '~'){
    

    p == NULL 时,path 的内容未定义在 IO 错误上,并且可能是 EOF 发生时的先前缓冲区内容。最好将返回类型从void 更改为char *,并在p == NULL 时返回。

    p = fgets(path, TEXT_SIZE, stdin);
    if (p == NULL) { 
      return NULL;
    }
    

    【讨论】:

    • 感谢您指出这个微妙的问题。我根据您的建议修改了代码,并接受了最有帮助的答案。
    猜你喜欢
    • 2011-11-27
    • 2013-08-01
    • 1970-01-01
    • 2015-07-18
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多