【问题标题】:Cocoa/Objective-C get a HFS path (path:to:desktop) from a posix path (path/to/desktop)Cocoa/Objective-C 从 posix 路径 (path/to/desktop) 获取 HFS 路径 (path:to:desktop)
【发布时间】:2017-12-18 06:59:03
【问题描述】:

我在 OSX,Objective-C 上。

我有一个类似的路径/NSURL

/Users/xxx/Desktop/image2.png

但我将它传递给第三方应用程序,该应用程序不包含类似的查找器路径

Harddisk:Users:Desktop:image2.png

是否有任何方法(我找不到)来转换这样的路径或将它们从 NSURL 中取出(如果可能,无需修改字符串)?

在 AppleScript 中是

return POSIX file "/Users/xxx/Desktop/image2.png" -->  Harddisk:Users:xxx:Desktop:image2.png

编辑:这几乎是一样的:Cocoa path string conversion 不幸的是,该方法已被弃用...

【问题讨论】:

    标签: objective-c path applescript nsurl applescript-objc


    【解决方案1】:

    目前没有(简单的)替代方案。

    函数CFURLCopyFileSystemPath 未被弃用,只有枚举案例kCFURLHFSPathStyle 被弃用,但原始值1 仍在工作并避免警告。

    我用的是NSString这个类别

    @implementation NSString (POSIX_HFS)
    
    - (NSString *)hfsPathFromPOSIXPath
    {
        CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)[NSURL fileURLWithPath:self], 1);
        return (NSString *)CFBridgingRelease(hfsPath);
    }
    @end
    

    该函数也适用于 Swift。 Swift 版本更复杂一些,并隐式添加了表示字典的尾随分号,这里作为 URL 的扩展:

    extension URL {
    
      func hfsPath() -> String?
      {
        if let cfpathHFS = CFURLCopyFileSystemPath(self as CFURL, CFURLPathStyle(rawValue: 1)!) { // CFURLPathStyle.CFURLHFSPathStyle)
          let pathHFS = cfpathHFS as String
          do {
            let info = try self.resourceValues(forKeys: [.isDirectoryKey, .isPackageKey])
            let isDirectory = info.isDirectory!
            let isPackage =  info.isPackage!
    
            if isDirectory && !isPackage {
              return pathHFS + ":" // directory, not package
            }
          } catch _ {}
          return pathHFS
        }
        return nil
      }
    }
    

    【讨论】:

    • 我刚刚发现,至少在 10.13.6 中,如果路径在 Finder 可见文件名中包含“/”,CFURLCopyFileSystemPath 不会创建正确的 HFS 路径。例如。 POSIX 路径“/Folder/FileWithColonAtEnd:”应该转换为“Volname:Folder:FileWithColonAtEnd/”,但会转换为“Volname:Folder:FileWithColonAtEnd”,或者最后一个字符相当随机的东西。
    【解决方案2】:

    Vadians 的答案比这个更好 - 但如果 vadians 方法被弃用,这将是另一种选择。想法是使用 applescripts 方法从 NSString 类别中使用 osascript 轻松获取 HFS 路径。

    NSString 类别(学分:https://stackoverflow.com/a/19014463/4591992)

    @implementation NSString (ShellExecution)
    
    - (NSString*)runAsCommand {
        NSPipe* pipe = [NSPipe pipe];
    
        NSTask* task = [[NSTask alloc] init];
        [task setLaunchPath: @"/bin/sh"];
        [task setArguments:@[@"-c", [NSString stringWithFormat:@"%@", self]]];
        [task setStandardOutput:pipe];
    
        NSFileHandle* file = [pipe fileHandleForReading];
        [task launch];
    
        NSString* result = [[NSString alloc] initWithData:[file readDataToEndOfFile] encoding:NSUTF8StringEncoding];
        return result;
    }
    
    @end
    

    本案例的用法:

    NSString* posixToHFS = [NSString stringWithFormat:@"osascript -e 'POSIX file \"%@\" as text'",filePath];
    filePath = [posixToHFS runAsCommand];
    

    【讨论】:

    • 就 AppleScript 而言:info for 的标准添加自 macOS 10.5 Leopard 起已弃用,但它仍在工作。所以呢? ;-)
    • NSAppleScript 可以执行 AppleScript。
    【解决方案3】:

    在我自己的测试中(在 10.13.6、10.14.6 和 10.15b7 上),@vadian 的解决方案不适用于文件夹名称组件包含“/”(在 Finder 中查看时)的路径,然后出现在 POSIX 路径中作为“:”,在 HFS 路径中作为“/”。

    错误演示

    这是一个快速测试程序,您可以通过在 Xcode 中创建一个新的“命令行”项目来构建它:

    #import <Foundation/Foundation.h>
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            NSString *posixPath = @"/EndsInSlash:";
            NSURL *url = [NSURL fileURLWithPath:posixPath];
            if (url == nil) {
                NSLog(@"Oops, this went wrong");
            } else {
                CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)url, 1);
                NSString *res = (NSString *)CFBridgingRelease (hfsPath);
                NSLog(@"HFS path: <%@>", res);
            }
        }
        return 0;
    }
    

    当您运行它时,您可能会看到打印的正确结果,即以“/”结尾的路径。但是,只有当文件夹不存在时才有效。因此,在您的根文件夹中创建一个名为“EndsInSlash/”的文件夹(不是文件!)并再次运行该应用程序 - 现在生成的路径不再以“/”结尾。

    解决方法

    下面是一个“智能”函数,它尽可能使用更快的CFURLCopyFileSystemPath() 函数,即除非“:”出现在 POSIX 路径中 - 在这种情况下,它通过拆分 POSIX 路径自行执行转换到其组件中,将它们单独转换(将“:”替换为“/”),添加卷名,然后再次合并组件。尽管有弃用警告,但这似乎在 macOS 10.15 (Catalina) 上也能正常工作。

    #pragma clang diagnostic push
    #pragma clang diagnostic ignored "-Wdeprecated-declarations"
    
    static NSString* stringWithHFSUniStr255(const HFSUniStr255* hfsString)
    {
        CFStringRef stringRef = FSCreateStringFromHFSUniStr(nil, hfsString);
        NSString* result = CFBridgingRelease(stringRef);
        return result;
    }
    
    NSString* hfsPathFromPOSIXPath (NSString *posixPath)
    {
        if (posixPath == nil) return @"";
        if ([posixPath containsString:@":"]) {
            // slow version, but can handle ":" appearing in path components
            NSString *result = nil;
            FSRef ref;
            Boolean isDir;
            if (FSPathMakeRef ((const UInt8*)posixPath.UTF8String, &ref, &isDir) == noErr) {
                HFSUniStr255 elemName;
                FSCatalogInfo catInfo;
                NSMutableArray<NSString*> *elems = [NSMutableArray arrayWithCapacity:16];
                while (FSGetCatalogInfo (&ref, kFSCatInfoNodeID, &catInfo, &elemName, nil, &ref) == noErr) {
                    [elems insertObject: stringWithHFSUniStr255(&elemName) atIndex:0];
                    if (catInfo.nodeID == 2) break;
                }
                result = [elems componentsJoinedByString:@":"];
            }
            return result;
        } else {
            // see https://stackoverflow.com/a/45085776/43615
            NSURL *url = [NSURL fileURLWithPath:posixPath];
            if (url == nil) {
                // could not convert because the path doesn't exist
                return nil;
            }
            CFStringRef hfsPath = CFURLCopyFileSystemPath((CFURLRef)url, kCFURLHFSPathStyle);
            return (NSString *)CFBridgingRelease (hfsPath);
        }
    }
    
    #pragma clang diagnostic pop
    

    另见

    讨论 AppleScript 的相关错误,并提供解决方法:https://forum.latenightsw.com/t/xxx/2097

    向 Apple 提交的错误报告:http://www.openradar.me/radar?id=4994410022436864

    【讨论】:

    • 看起来 NSString 的 stringByStandardizingPath 以不一致的方式在幕后使用 - 这将删除尾部斜杠。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-01
    • 1970-01-01
    相关资源
    最近更新 更多