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
我在 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