web-dev-qa-db-fra.com

Comment diviser un nom de fichier en extension de fichier dans Swift?

Étant donné le nom d'un fichier dans le paquet, je veux charger le fichier dans mon application Swift. J'ai donc besoin d'utiliser cette méthode:

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

Pour une raison quelconque, la méthode nécessite le nom de fichier séparé de l'extension de fichier. Bien, il est assez facile de séparer les deux dans la plupart des langues. Mais jusqu'à présent, je ne le trouve pas à Swift.

Alors voici ce que j'ai:

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

Si je n'inclue pas la saisie sur la première ligne, des erreurs apparaissent sur les deux lignes suivantes. Avec cela, je reçois une erreur sur la première ligne:

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

Comment puis-je séparer le nom du fichier de l'extension? Y a-t-il une manière élégante de faire ceci?

Swift me passionnait à tous les égards car il semblait être un langage beaucoup plus élégant que l’objectif C. Mais à présent, je constate qu’il a sa propre lourdeur.


Deuxième tentative: j'ai décidé de créer ma propre méthode de recherche de chaînes:

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
}

Mais maintenant, je reçois une erreur sur la ligne var rt: String.Index = rfind(fileName, needle: "."):

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

Sans le casting, j'obtiens une erreur sur les deux lignes suivantes.

Quelqu'un peut-il m'aider à scinder ce nom de fichier et cette extension?

42
JohnK

C’est avec Swift 2, Xcode 7: Si vous avez déjà le nom de fichier avec l’extension, vous pouvez alors passer le nom de fichier complet en tant que premier paramètre et une chaîne vide en tant que second paramètre:

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

Sinon, nil en tant que paramètre d'extension fonctionne également.

Si vous avez une URL et que vous souhaitez obtenir le nom du fichier lui-même pour une raison quelconque, procédez comme suit:

soundURL.URLByDeletingPathExtension?.lastPathComponent

Swift 4

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

Comme indiqué dans le commentaire, vous pouvez l'utiliser.

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

Fonctionne avec Swift 3/Swift 4. Ajout de ces comportements à la classe String:

extension String {

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

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

Exemple:

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

Dans Swift 2.1, String.pathExtension n'est plus disponible. Au lieu de cela, vous devez le déterminer par la conversion NSURL:

NSURL(fileURLWithPath: filePath).pathExtension
28
Santanu Karar

Solution Swift 4

Cette solution fonctionnera pour toutes les instances et ne dépend pas de l'analyse manuelle de la chaîne.

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

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

Swift.print(fileName)

La sortie résultante sera

This.Strange.File
23
Lore

Dans Swift, vous pouvez passer à NSString pour obtenir une extension plus rapidement:

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

Dans Swift 2.1, il semble que la manière actuelle de faire cela est:

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

Solution native la plus courte 3.x de Swift

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

Aucune extension ni aucun élément supplémentaire (J'ai testé. Basé sur la solution @gabbler pour Swift 2)

3
Trevor

Les cordes dans Swift peuvent certainement être délicates. Si vous voulez une méthode Swift pure, voici comment je le ferais:

  1. Utilisez find pour trouver la dernière occurrence d'un "." dans le reverse de la chaîne
  2. Utilisez advance pour obtenir le bon index du "." dans la chaîne d'origine
  3. Utilisez la fonction String de subscript qui prend une IntervalType pour obtenir les chaînes
  4. Regrouper tout cela dans une fonction qui retourne un tuple optionnel du nom et de l'extension

Quelque chose comme ça:

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
}

Ce qui serait utilisé comme:

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

Quelles sorties:

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

Ou bien, utilisez simplement les méthodes déjà disponibles NSString like pathExtension et stringByDeletingPathExtension .

2
Mike S

Essayez ceci pour une solution simple 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
Eng Yew

La dernière version de Swift 4.2 fonctionne comme ceci:

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

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

Swift 3.x extended solution:

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)

la constante de nom de fichier contient maintenant "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

Une réponse nettoyée pour Swift 4 avec une extension de 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
    }
}

Comme indiqué dans XCode, le nom de fichier d'origine est le nom de l'actif au moment de sa création ou de son importation.

0
CodeBender

Crée une URL de formulaire "nom de fichier" unique comprenant deux dossiers précédents

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

Un meilleur moyen (ou au moins une alternative dans Swift 2.0) consiste à utiliser la propriété String pathComponents. Cela divise le chemin en un tableau de chaînes. par exemple

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

Peut-être que je commence trop tard pour cela, mais une solution qui fonctionne pour moi et que je considère assez simple consiste à utiliser la directive de compilation #file. Voici un exemple où j'ai une classe FixtureManager, définie dans FixtureManager.Swift à l'intérieur du/Tests/MyProjectTests/Fixturesdirectory. This works both in Xcode and withSwift test`

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

Ils se sont débarrassés de pathExtension pour une raison quelconque.

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