如何在Swift中从文件扩展名中拆分文件名?


74

给定捆绑中文件的名称,我想将该文件加载到我的Swift应用程序中。所以我需要使用这种方法:

let soundURL = NSBundle.mainBundle().URLForResource(fname, withExtension: ext)

无论出于何种原因,该方法都需要将文件名与文件扩展名分开。很好,在大多数语言中将两者分开很容易。但是到目前为止,在Swift中我还没有发现。

所以这是我所拥有的:

var rt: String.Index = fileName.rangeOfString(".", options:NSStringCompareOptions.BackwardsSearch)
var fname: String = fileName .substringToIndex(rt)
var ext = fileName.substringFromIndex(rt)

如果我在第一行中不包含键入内容,则在随后的两行中都会出现错误。有了它,我在第一行得到一个错误:

Cannot convert the expression's type '(UnicodeScalarLiteralConvertible, options: NSStringCompareOptions)' to type 'UnicodeScalarLiteralConvertible'

如何从扩展名中拆分文件名?有一些优雅的方法可以做到这一点吗?

我对Swift感到非常兴奋,因为它看起来比Objective C优雅得多。但是现在我发现它有它自己的麻烦。


第二次尝试:我决定制作自己的字符串搜索方法:

func rfind(haystack: String, needle: Character) -> Int {
    var a = Array(haystack)

    for var i = a.count - 1; i >= 0; i-- {
        println(a[i])
        if a[i] == needle {
            println(i)
            return i;
        }
    }
    return -1
}

但是现在我在网上遇到了一个错误var rt: String.Index = rfind(fileName, needle: ".")

'Int' is not convertible to 'String.Index'

如果不进行强制转换,则在随后的两行中都会出现错误。

谁能帮我拆分此文件名和扩展名?


您是否看过专门用于处理路径的NSString方法(例如lastPathComponent和stringByDeletingPathExtension)?
rdelmar

7
您知道,如果我们Objective-C用户不得不忍受对文档进行的故意破坏以合并Swift,那么如果您看过Swifties,那就很好了。
热门点击2014年

Answers:


83

这与Swift 2,Xcode 7一起使用:如果您已经具有扩展名的文件名,则可以将完整的文件名作为第一个参数传递,并将空白字符串作为第二个参数传递:

let soundURL = NSBundle.mainBundle()
    .URLForResource("soundfile.ext", withExtension: "")

另外,nil作为扩展参数也可以使用。

如果您有一个URL,并且由于某种原因想要获取文件本身的名称,则可以执行以下操作:

soundURL.URLByDeletingPathExtension?.lastPathComponent

斯威夫特4

let soundURL = NSBundle.mainBundle().URLForResource("soundfile.ext", withExtension: "")
soundURL.deletingPathExtension().lastPathComponent

这应该是推荐的答案!完美运作
Ipad 2015年

实际上,据记载,您可以传递整个文件名,而将扩展名保留为nil或为空:developer.apple.com/library/mac/documentation/Cocoa/Reference/…
TruMan1 2016年

100

Swift 5.0更新:

如评论中指出的,您可以使用它。

let filename: NSString = "bottom_bar.png"
let pathExtention = filename.pathExtension
let pathPrefix = filename.deletingPathExtension

1
由于Swift的String类型无缝地连接到Foundation的NSString类,因此无需创建新的NSString。只需使用let pathExtension = filename.pathExtension和即可let pathPrefix = filename.stringByDeletingPathExtension
詹姆斯·哈德斯顿

6
为了访问stringByDeletingPathExtension,您必须将文件名强制转换为NSString-让pathPrefix =(文件名作为NSString).stringByDeletingPathExtension。对于pathExtension也是如此。
user1021430 2015年

1
更新更改的名称:stringByDeletingPathExtension现在正在删除PathExtension
Cameron Lowell

看来您必须let filename: NSString = "bottom_bar.png" as NSString现在。
维克多·恩格尔

1
应该是let pathPrefix = filename.deletingPathExtension
亚当

59

在Swift 5中可以使用。将这些行为添加到String类中:

extension String {

    func fileName() -> String {
        return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent 
    }

    func fileExtension() -> String {
        return URL(fileURLWithPath: self).pathExtension
    }
}

例:

let file = "image.png"
let fileNameWithoutExtension = file.fileName()
let fileExtension = file.fileExtension()

4
只是样式,但我更喜欢这种变化:public var fileExtension: String { return NSURL(fileURLWithPath: self).pathExtension.lowercaseString ?? "" }//假设我们希望.JPG Yahoos不区分大小写
BaseZen

2
在Swift 3中,您应该使用URLnot NSURL
Sulthan

31

解决方案Swift 4

此解决方案将适用于所有实例,并且不依赖于手动解析字符串。

let path = "/Some/Random/Path/To/This.Strange.File.txt"

let fileName = URL(fileURLWithPath: path).deletingPathExtension().lastPathComponent

Swift.print(fileName)

结果输出将是

This.Strange.File

这节省了我很多自定义格式的时间,谢谢!
Menno

27

在Swift 2.1中,String.pathExtension不再可用。相反,您需要通过NSURL转换来确定它:

NSURL(fileURLWithPath: filePath).pathExtension

但是,NSString.pathExtension它仍然可用,因此您只需将其转换为NSString:即可(pathString as NSString).pathExtension
杰米·伯奇

这很好,但是最好使用URLclass而不是NSURL
Vahid Amiri

13

在Swift中,您可以更改为NSString以获得扩展:

extension String {
    func getPathExtension() -> String {
        return (self as NSString).pathExtension
    }
}

5

在Swift 2.1中,当前的执行方式似乎是:

let filename = fileURL.URLByDeletingPathExtension?.lastPathComponent
let extension = fileURL.pathExtension

5

最新的Swift 4.2的工作方式如下:

extension String {
    func fileName() -> String {
        return URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
    }

    func fileExtension() -> String {
        return URL(fileURLWithPath: self).pathExtension
    }
}

3

Swift中的字符串肯定很棘手。如果您要使用纯Swift方法,请按以下步骤操作:

  1. 使用find找到的最后一次出现"."reverse字符串
  2. 使用advance得到的正确指数"."在原始字符串
  3. 使用需要Stringsubscript函数IntervalType来获取字符串
  4. 将所有内容打包到一个函数中,该函数返回名称和扩展名的可选元组

像这样:

func splitFilename(str: String) -> (name: String, ext: String)? {
    if let rDotIdx = find(reverse(str), ".") {
        let dotIdx = advance(str.endIndex, -rDotIdx)
        let fname = str[str.startIndex..<advance(dotIdx, -1)]
        let ext = str[dotIdx..<str.endIndex]
        return (fname, ext)
    }
    return nil
}

使用方法如下:

let str = "/Users/me/Documents/Something.something/text.txt"
if let split = splitFilename(str) {
    println(split.name)
    println(split.ext)
}

哪个输出:

/Users/me/Documents/Something.something/text
txt

或者,仅使用现有的NSString方法,pathExtensionstringByDeletingPathExtension


3

SWIFT 3.x最短的本机解决方案

let fileName:NSString = "the_file_name.mp3"
let onlyName = fileName.deletingPathExtension
let onlyExt = fileName.pathExtension

没有扩展名或任何其他内容 (我已经测试过。基于Swift 2的@gabbler解决方案)


3

迅捷5

 URL(string: filePath)?.pathExtension

2

试试这个简单的Swift 4解决方案

extension String {
    func stripExtension(_ extensionSeperator: Character = ".") -> String {
        let selfReversed = self.reversed()
        guard let extensionPosition = selfReversed.index(of: extensionSeperator) else {  return self  }
        return String(self[..<self.index(before: (extensionPosition.base.samePosition(in: self)!))])
    }
}

print("hello.there.world".stripExtension())
// prints "hello.there"

2

带有代码糖的Swift 5

extension String {
    var fileName: String {
       URL(fileURLWithPath: self).deletingPathExtension().lastPathComponent
    }

    var fileExtension: String{
       URL(fileURLWithPath: self).pathExtension
    }
}

1

斯威夫特3.0

 let sourcePath = NSURL(string: fnName)?.pathExtension
 let pathPrefix = fnName.replacingOccurrences(of: "." + sourcePath!, with: "")

1

Swift 3.x扩展解决方案:

extension String {
    func lastPathComponent(withExtension: Bool = true) -> String {
        let lpc = self.nsString.lastPathComponent
        return withExtension ? lpc : lpc.nsString.deletingPathExtension
    }

    var nsString: NSString {
         return NSString(string: self)
    }
}

let path = "/very/long/path/to/filename_v123.456.plist"
let filename = path.lastPathComponent(withExtension: false)

文件名常量现在包含“ filename_v123.456


0

更好的方法(或至少是Swift 2.0中的替代方法)是使用String pathComponents属性。这会将路径名拆分为字符串数组。例如

if let pathComponents = filePath.pathComponents {
    if let last = pathComponents.last {
        print(" The last component is \(last)") // This would be the extension
        // Getting the last but one component is a bit harder
        // Note the edge case of a string with no delimiters!
    }
}
// Otherwise you're out of luck, this wasn't a path name!

0

无论出于什么原因,他们都放弃了pathExtension。

let str = "Hello/this/is/a/filepath/file.ext"
let l = str.componentsSeparatedByString("/")
let file = l.last?.componentsSeparatedByString(".")[0]
let ext = l.last?.componentsSeparatedByString(".")[1]

1
仅当文件名仅包含一个点时才有效。没有更多的点工作,没有他们崩溃..
JMI

如果没有任何句点,则文件没有适当的扩展名。如果存在多个句点,则可以通过让ext = l.last?.componentsSeparatedByString(“。”)。last获得扩展名。
Pescolly

0

Swift 4的清理答案,扩展为PHAsset

import Photos

extension PHAsset {
    var originalFilename: String? {
        if #available(iOS 9.0, *),
            let resource = PHAssetResource.assetResources(for: self).first {
            return resource.originalFilename
        }

        return value(forKey: "filename") as? String
    }
}

如XCode所述,originalFilename是资产在创建或导入时的名称。


0

也许我为时已晚,但是一个对我有用并且认为很简单的解决方案是使用#file编译器指令。这是一个我在/ Tests / MyProjectTests / Fixtures swift测试内部FixtureManager定义的类的示例。FixtureManager.swiftdirectory. This works both in Xcode and with

import Foundation

final class FixtureManager {

    static let fixturesDirectory = URL(fileURLWithPath: #file).deletingLastPathComponent()

    func loadFixture(in fixturePath: String) throws -> Data {
        return try Data(contentsOf: fixtureUrl(for: fixturePath))
    }

    func fixtureUrl(for fixturePath: String) -> URL {
        return FixtureManager.fixturesDirectory.appendingPathComponent(fixturePath)
    }

    func save<T: Encodable>(object: T, in fixturePath: String) throws {
        let data = try JSONEncoder().encode(object)
        try data.write(to: fixtureUrl(for: fixturePath))
    }

    func loadFixture<T: Decodable>(in fixturePath: String, as decodableType: T.Type) throws -> T {
        let data = try loadFixture(in: fixturePath)
        return try JSONDecoder().decode(decodableType, from: data)
    }

}

0

创建包含两个先前文件夹的唯一“文件名”表单网址

func createFileNameFromURL (colorUrl: URL) -> String {

    var arrayFolders = colorUrl.pathComponents

    // -3 because last element from url is "file name" and 2 previous are folders on server
    let indx = arrayFolders.count - 3
    var fileName = ""

    switch indx{
    case 0...:
        fileName = arrayFolders[indx] + arrayFolders[indx+1] + arrayFolders[indx+2]
    case -1:
        fileName = arrayFolders[indx+1] + arrayFolders[indx+2]
    case -2:
        fileName = arrayFolders[indx+2]
    default:
        break
    }


    return fileName
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.