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

时间:2017-07-13 13:07:35

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

我在OSX,Objective-C。

我有路径/ NSURL喜欢

/Users/xxx/Desktop/image2.png

但是我将它传递给第三方应用程序,该应用程序可以感知像

这样的查找程序
Harddisk:Users:Desktop:image2.png

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

在AppleScript中它是

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

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

3 个答案:

答案 0 :(得分:2)

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

不推荐使用函数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
  }
}

答案 1 :(得分:1)

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

答案 2 :(得分:0)

在我自己的测试中(在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

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