Cocoa/Objective-C 从 posix 路径 (path/to/desktop) 获取 HFS 路径 (path:to:desktop)

Cocoa/Objective-C get a HFS path (path:to:desktop) from a posix path (path/to/desktop)

我在 OSX, Objective-C.

我有一个 path/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 不幸的是,该方法已被弃用...

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

函数 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
  }
}

Vadians 的回答比这个更好 - 但如果 vadians 方法被弃用,这将是一个替代方法。想法是使用 applescripts 方法通过 NSString 类别中的 osascript 轻松调用 HFS 路径。

NSString 类别(学分:)

@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];

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

错误演示

这是一个快速测试程序,您可以通过在 Xcode 中创建一个新的 "command line" 项目来构建它:

#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/" 的文件夹(不是文件!),然后再次 运行 应用程序 - 现在生成的路径不再以“/”结尾。

解决方法

下面是一个 "smart" 函数,它尽可能使用更快的 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 
        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