Cómo dividir el nombre de archivo de la extensión de archivo en Swift?

Dado el nombre de un archivo en el paquete, quiero cargar el archivo en mi aplicación Swift. Entonces necesito usar este método:

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

Por alguna razón, el método necesita el nombre del archivo separado de la extensión del archivo. Bien, es bastante fácil separar los dos en la mayoría de los idiomas. Pero hasta ahora no creo que sea así en Swift.

Entonces aquí está lo que tengo:

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

Si no incluyo el tipeo en la primera línea, recibo errores en las dos líneas siguientes. Con eso, recibo un error en la primera línea:

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

¿Cómo puedo dividir el nombre de archivo de la extensión? ¿Hay alguna forma elegante de hacer esto?

Estaba entusiasmado con Swift porque me pareció un lenguaje mucho más elegante que Objective C. Pero ahora descubro que tiene su propio engorro.


Segundo bash: decidí hacer mi propio método de búsqueda de cadenas:

 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 } 

Pero ahora var rt: String.Index = rfind(fileName, needle: ".") un error en la línea var rt: String.Index = rfind(fileName, needle: ".") :

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

Sin el elenco, recibo un error en las dos líneas siguientes.

¿Alguien puede ayudarme a dividir este nombre de archivo y extensión?

Esto es con Swift 2, Xcode 7: si tiene el nombre de archivo con la extensión ya en él, puede pasar el nombre de archivo completo como primer parámetro y una cadena en blanco como segundo parámetro:

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

Alternativamente nil como el parámetro de extensión también funciona.

Si tiene una URL y desea obtener el nombre del archivo por algún motivo, puede hacerlo:

 soundURL.URLByDeletingPathExtension?.lastPathComponent 

Swift 4

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

Como se señaló en el comentario, puede usar esto.

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

Funciona con Swift 3 / Swift 4. Agregando estos comportamientos a String clase String :

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

Ejemplo:

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

En Swift 2.1 String.pathExtension ya no está disponible. En su lugar, debe determinarlo a través de la conversión NSURL:

 NSURL(fileURLWithPath: filePath).pathExtension 

Solución Swift 4

Esta solución funcionará para todas las instancias y no depende de analizar manualmente la cadena.

 let path = "/Some/Random/Path/To/This.Strange.File.txt" let fileName = URL(fileURLWithPath: path).deletingPathExtension().lastPathComponent Swift.print(fileName) 

El resultado resultante será

 This.Strange.File 

En Swift puede cambiar a NSString para obtener una extensión más rápida:

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

En Swift 2.1, parece que la forma actual de hacerlo es:

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

SWIFT 3.x La solución nativa más corta

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

Sin extensión ni nada adicional (lo he probado, basado en la solución @gabbler para Swift 2)

Las cadenas en Swift definitivamente pueden ser difíciles. Si quieres un método Swift puro, así es como lo haría:

  1. Use find para encontrar la última aparición de un "." en el reverse de la cuerda
  2. Use advance para obtener el índice correcto de "." en la cadena original
  3. Use String función de subscript String que toma un IntervalType para obtener las cadenas
  4. Empaque todo esto en una función que devuelve una tupla opcional del nombre y la extensión

Algo como esto:

 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.. 

Que se usaría como:

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

Qué salidas:

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

O simplemente use los métodos NSString ya disponibles como pathExtension y stringByDeletingPathExtension .

Prueba esto para una solución simple de 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[.. 

Swift 3.0

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

Swift 3.x solución extendida:

 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 nombre de archivo ahora contiene ” filename_v123.456

Una mejor forma (o al menos una alternativa en Swift 2.0) es usar la propiedad String pathComponents. Esto divide el nombre de ruta en una matriz de cadenas. p.ej

 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! 

Se deshicieron de PathExtension por la razón que sea.

 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] 

Una respuesta limpia para Swift 4 con una extensión 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 } } 

Como se indica en XCode, originalFilename es el nombre del activo en el momento en que fue creado o importado.

Tal vez me estoy poniendo demasiado tarde para esto, pero una solución que funcionó para mí y que considero bastante simple es el uso de la directiva del comstackdor #file . Aquí hay un ejemplo donde tengo una clase FixtureManager , definida en FixtureManager.swift dentro del directory. This works both in Xcode and with / Tests / MyProjectTests / Fixtures directory. This works both in Xcode and with directory. This works both in Xcode and with una prueba rápida`

 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(object: T, in fixturePath: String) throws { let data = try JSONEncoder().encode(object) try data.write(to: fixtureUrl(for: fixturePath)) } func loadFixture(in fixturePath: String, as decodableType: T.Type) throws -> T { let data = try loadFixture(in: fixturePath) return try JSONDecoder().decode(decodableType, from: data) } } 

Crea url único de formulario de “nombre de archivo” que incluye dos carpetas anteriores

 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 }