wake-up-neo.com

Wie kann ich Dateinamen von der Dateiendung in Swift trennen?

Wenn Sie den Namen einer Datei im Bundle erhalten, möchte ich die Datei in meine Swift-App laden. Also muss ich diese Methode verwenden:

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

Aus irgendeinem Grund benötigt die Methode den Dateinamen getrennt von der Dateierweiterung. Gut, es ist leicht genug, die beiden in den meisten Sprachen zu trennen. Aber bis jetzt finde ich es nicht so in Swift.

Also hier ist was ich habe:

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

Wenn ich die Eingabe nicht in die erste Zeile einbeziehe, erhalte ich in den beiden folgenden Zeilen Fehler. Damit bekomme ich einen Fehler in der ersten Zeile:

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

Wie kann ich den Dateinamen von der Erweiterung trennen? Gibt es eine elegante Möglichkeit, dies zu tun?

Ich war ganz begeistert von Swift, weil es wie eine viel elegantere Sprache schien als Objective C. Aber jetzt finde ich, dass es eine eigene Schwerfälligkeit hat.


Zweiter Versuch: Ich entschied mich für meine eigene Suchmethode:

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
}

Aber jetzt bekomme ich einen Fehler in der Zeile var rt: String.Index = rfind(fileName, needle: "."):

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

Ohne die Besetzung bekomme ich einen Fehler in den beiden folgenden Zeilen.

Kann mir jemand helfen, diesen Dateinamen und die Erweiterung zu teilen?

42
JohnK

Dies ist mit Swift 2, Xcode 7: Wenn Sie den Dateinamen mit der Erweiterung bereits haben, können Sie den vollständigen Dateinamen als ersten Parameter und eine leere Zeichenfolge als zweiten Parameter übergeben:

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

Alternativ funktioniert auch nil als Erweiterungsparameter.

Wenn Sie eine URL haben und aus irgendeinem Grund den Namen der Datei selbst erhalten möchten, können Sie Folgendes tun:

soundURL.URLByDeletingPathExtension?.lastPathComponent

Swift 4

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

Wie im Kommentar erwähnt, können Sie dies verwenden.

let filename: NSString = "bottom_bar.png"
let pathExtention = filename.pathExtension
let pathPrefix = filename.stringByDeletingPathExtension
80
gabbler

Funktioniert mit Swift 3/Swift 4. Hinzufügen dieses Verhaltens zur String-Klasse:

extension String {

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

    func fileExtension() -> String {
        return NSURL(fileURLWithPath: self).pathExtension ?? ""
    }
}

Beispiel:

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

In Swift 2.1 ist String.pathExtension nicht mehr verfügbar. Stattdessen müssen Sie es durch NSURL-Konvertierung ermitteln:

NSURL(fileURLWithPath: filePath).pathExtension
28
Santanu Karar

Solution Swift 4

Diese Lösung funktioniert für alle Instanzen und ist nicht von der manuellen Analyse der Zeichenfolge abhängig.

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

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

Swift.print(fileName)

Die resultierende Ausgabe wird sein

This.Strange.File
23
Lore

In Swift können Sie zu NSString wechseln, um die Erweiterung schneller zu machen:

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

In Swift 2.1 scheint dies der aktuelle Weg zu sein:

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

Swift 3.x kürzeste native Lösung

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

Keine Erweiterung oder sonstiges Zeug (habe ich getestet. Basierend auf @gabbler-Lösung für Swift 2)

3
Trevor

Strings in Swift können durchaus knifflig sein. Wenn Sie eine reine Swift-Methode wünschen, würde ich Folgendes tun:

  1. Verwenden Sie find , um das letzte Vorkommen eines "." in der reverse der Zeichenfolge zu finden
  2. Verwenden Sie advance , um den korrekten Index des "." in der Originalzeichenfolge abzurufen
  3. Verwenden Sie die String-Funktion von subscript, die eine IntervalType-Anweisung benötigt, um die Zeichenfolgen abzurufen
  4. Packen Sie dies alles in eine Funktion, die ein optionales Tuple des Namens und der Erweiterung zurückgibt

Etwas wie das:

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
}

Welche würde verwendet werden wie:

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

Welche Ausgänge:

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

Oder verwenden Sie einfach die bereits verfügbaren NSString-Methoden wie pathExtension und stringByDeletingPathExtension .

2
Mike S

Versuchen Sie dies für eine einfache Swift 4-Lösung

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
Eng Yew

Das neueste Swift 4.2 funktioniert so:

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

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

Swift 3.x erweiterte Lösung:

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)

dateiname Konstante enthält jetzt "filename_v123.456"

Swift 5

 URL(string: filePath)?.pathExtension
1
Den

Swift 3.0 

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

Eine bereinigte Antwort für Swift 4 mit einer Erweiterung von 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
    }
}

Wie in XCode angegeben, ist der ursprüngliche Dateiname der Name des Assets zum Zeitpunkt der Erstellung oder des Imports.

0
CodeBender

Erzeugt einen eindeutigen "Dateinamen" -Formular mit zwei vorherigen Ordnern

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
}
0
Pvlub

Eine bessere Möglichkeit (oder zumindest eine Alternative in Swift 2.0) ist die Verwendung der String pathComponents -Eigenschaft. Dadurch wird der Pfadname in ein String-Array aufgeteilt. z.B

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
Derek Knight

Vielleicht komme ich dafür zu spät, aber eine Lösung, die für mich funktioniert hat und die ich für ziemlich einfach halte, ist die #file-Compiler-Direktive. Hier ist ein Beispiel, wo ich eine Klasse FixtureManager habe, die in FixtureManager.Swift innerhalb des/Tests/MyProjectTests/Fixturesdirectory. This works both in Xcode and withSwift-Tests definiert ist

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
GuidoMB

Aus irgendeinem Grund wurden sie pathExtension entfernt.

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]
0
Pescolly