check Filename is exist by prefix in Swift - swift

I want to check whether my filename with just prefix is exist or not in Swift.
E.g
My file name is like Companies_12344
So after _ values are dynamic but "Companies_" is static.
How can i do that?
My code below For split
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)]
println("splitFilename >> Split File Name >>\(fname)")
}
return nil
}

It's not very clear what you want to do, because your code snippet already does check if the string has the prefix.
There's a simpler way, though:
let fileName = "Companies_12344"
if fileName.hasPrefix("Companies") {
println("Yes, this one has 'Companies' as a prefix")
}
Swift's hasPrefix method checks if the string begins with the specified string.
Also, you could split the string easily with this:
let compos = fileName.componentsSeparatedByString("_") // ["Companies", "12344"]
Then you could check if there's a code and grab it with:
if let fileCode = compos.last {
println("There was a code after the prefix: \(fileCode)")
}

Related

Split String or Substring with Regex pattern in Swift

First let me point out... I want to split a String or Substring with any character that is not an alphabet, a number, # or #. That means, I want to split with whitespaces(spaces & line breaks) and special characters or symbols excluding # and #
In Android Java, I am able to achieve this with:
String[] textArr = text.split("[^\\w_##]");
Now, I want to do the same in Swift. I added an extension to String and Substring classes
extension String {}
extension Substring {}
In both extensions, I added a method that returns an array of Substring
func splitWithRegex(by regexStr: String) -> [Substring] {
//let string = self (for String extension) | String(self) (for Substring extension)
let regex = try! NSRegularExpression(pattern: regexStr)
let range = NSRange(string.startIndex..., in: string)
return regex.matches(in: string, options: .anchored, range: range)
.map { match -> Substring in
let range = Range(match.range(at: 1), in: string)!
return string[range]
}
}
And when I tried to use it, (Only tested with a Substring, but I also think String will give me the same result)
let textArray = substring.splitWithRegex(by: "[^\\w_##]")
print("substring: \(substring)")
print("textArray: \(textArray)")
This is the out put:
substring: This,is a #random #text written for debugging
textArray: []
Please can Someone help me. I don't know if the problem if from my regex [^\\w_##] or from splitWithRegex method
The main reason why the code doesn't work is range(at: 1) which returns the content of the first captured group, but the pattern does not capture anything.
With just range the regex returns the ranges of the found matches, but I suppose you want the characters between.
To accomplish that you need a dynamic index starting at the first character. In the map closure return the string from the current index to the lowerBound of the found range and set the index to its upperBound. Finally you have to add manually the string from the upperBound of the last match to the end.
The Substring type is a helper type for slicing strings. It should not be used beyond a temporary scope.
extension String {
func splitWithRegex(by regexStr: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regexStr) else { return [] }
let range = NSRange(startIndex..., in: self)
var index = startIndex
var array = regex.matches(in: self, range: range)
.map { match -> String in
let range = Range(match.range, in: self)!
let result = self[index..<range.lowerBound]
index = range.upperBound
return String(result)
}
array.append(String(self[index...]))
return array
}
}
let text = "This,is a #random #text written for debugging"
let textArray = text.splitWithRegex(by: "[^\\w_##]")
print(textArray) // ["This", "is", "a", "#random", "#text", "written", "for", "debugging"]
However in macOS 13 and iOS 16 there is a new API quite similar to the java API
let text = "This,is a #random #text written for debugging"
let textArray = Array(text.split(separator: /[^\w_##]/))
print(textArray)
The forward slashes indicate a regex literal

Check existence of localized string in .strings resource file, fall back to default

I want to find the appropriate localized string based on some runtime variable and fall back to a default string:
// localizable.strings
"com.myapp.text1" = "The default text 1";
"com.myapp.text1#SPECIAL" = "The special text";
"com.myapp.text2" = "The default text 2";
// my code
let key1 = "com.myapp.text1"
let key2 = "com.myapp.text2"
let modifier = "#SPECIAL"
print( NSLocalizedString(key1 + modifier
, value: NSLocalizedString(key1, comment: "")
, comment: "") )
// > "The special text 1"
print( NSLocalizedString(key2 + modifier
, value: NSLocalizedString(key2, comment: "") # the default to fall back to
, comment: "") )
// > "The default text 2"
Nice, that's what I want, try a special variant, fall back to the default.
However, if the option NSShowNonLocalizedStrings in the user defaults is set to true, it fails:
For non-localised strings, an upper-case version of the key will be returned, ignoring the default value. Also an error message is printed in the console (documentation).
So it appears that my solution is working against intended way of using NSLocalizedString.
UserDefaults.standard.set(true, forKey: "NSShowNonLocalizedStrings") # could also be passed as launch option or set via terminal
print( NSLocalizedString(key2 + modifier
, value: NSLocalizedString(key2, comment: "")
, comment: "") )
// > ERROR: com.myapp.text2#MODIFIER not found [...]
// > "COM.MYAPP.TEXT2"
I could work around this by testing for the uppercased version etc. but this would just be a hack that masks the actual issue.
What I would probably need is a test if (bundle.hasLocalizationFor(key2 + modifier)... but to implement such a method I would have to re-implement processing of the strings files including parsing and caching. And that feels wrong in itself.
Question:
Is there some method I am missing to achieve what I am looking for?
Or is the whole idea of special/fallback localization just wrong for the platform?
Thanks to comments by Martin R, I was able to get a reasonably working solution:
static let cache = NSCache<NSString, NSDictionary>()
private func strings(for tableName: String) -> [String: String] {
guard let strings = cache.object(forKey: tableName as NSString) else {
guard let path = Bundle.main.path(forResource: tableName, ofType: "strings")
, let dict = NSDictionary(contentsOfFile: path) as? [String: String] else {
return [:]
}
cache.setObject(dict as NSDictionary, forKey: tableName as NSString)
return dict
}
return strings as! [String: String]
}
func localizedString(_ key: String) -> String {
let specificKey = key + "#SPECIAL"
let tableName = "TableName"
let bundle = Bundle.main
return self.strings(for: tableName)[specificKey]
?? NSLocalizedString(key
, tableName: tableName
, bundle: bundle
, comment: "")
}

Remove suffix from filename in Swift

When trying to remove the suffix from a filename, I'm only left with the suffix, which is exactly not what I want.
What (how many things) am I doing wrong here:
let myTextureAtlas = SKTextureAtlas(named: "demoArt")
let filename = (myTextureAtlas.textureNames.first?.characters.split{$0 == "."}.map(String.init)[1].replacingOccurrences(of: "\'", with: ""))! as String
print(filename)
This prints png which is the most dull part of the whole thing.
If by suffix you mean path extension, there is a method for this:
let filename = "demoArt.png"
let name = (filename as NSString).deletingPathExtension
// name - "demoArt"
Some people here seem to overlook that a filename can have multiple periods in the name and in that case only the last period separates the file extension. So this.is.a.valid.image.filename.jpg and stripping the extension should return this.is.a.valid.image.filename and not this (as two answers here would produce) or anything else in between. The regex answer works correctly but using a regex for that is a bit overkill (probably 10 times slower than using simple string processing). Here's a generic function that works for everyone:
func stripFileExtension ( _ filename: String ) -> String {
var components = filename.components(separatedBy: ".")
guard components.count > 1 else { return filename }
components.removeLast()
return components.joined(separator: ".")
}
print("1: \(stripFileExtension("foo"))")
print("2: \(stripFileExtension("foo.bar"))")
print("3: \(stripFileExtension("foo.bar.foobar"))")
Output:
foo
foo
foo.bar
You can also split the String using componentsSeparatedBy, like this:
let fileName = "demoArt.png"
var components = fileName.components(separatedBy: ".")
if components.count > 1 { // If there is a file extension
components.removeLast()
return components.joined(separator: ".")
} else {
return fileName
}
To clarify:
fileName.components(separatedBy: ".")
will return an array made up of "demoArt" and "png".
In iOS Array start with 0 and you want name of the file without extension, so you have split the string using ., now the name will store in first object and extension in the second one.
Simple Example
let fileName = "demoArt.png"
let name = fileName.characters.split(".").map(String.init).first
If you don't care what the extension is. This is a simple way.
let ss = filename.prefix(upTo: fileName.lastIndex { $0 == "." } ?? fileName.endIndex))
You may want to convert resulting substring to String after this. With String(ss)
#Confused with Swift 4 you can do this:
let fileName = "demoArt.png"
// or on your specific case:
// let fileName = myTextureAtlas.textureNames.first
let name = String(fileName.split(separator: ".").first!)
print(name)
Additionally you should also unwrapp first but I didn't want to complicate the sample code to solve your problem.
Btw, since I've also needed this recently, if you want to remove a specific suffix you know in advance, you can do something like this:
let fileName = "demoArt.png"
let fileNameExtension = ".png"
if fileName.hasSuffix(fileNameExtension) {
let name = fileName.prefix(fileName.count - fileNameExtension.count)
print(name)
}
How about using .dropLast(k) where k is the number of characters you drop from the suffix ?
Otherwise for removing extensions from path properly from filename, I insist you to use URL and .deletingPathExtension().lastPathComponent.
Maybe a bit overhead but at least it's a rock solid Apple API.
You can also use a Regexp to extract all the part before the last dot like that :
let fileName = "test.png"
let pattern = "^(.*)(\\.[a-zA-Z]+)$"
let regexp = try! NSRegularExpression(pattern: pattern, options: [])
let extractedName = regexp.stringByReplacingMatches(in: fileName, options: [], range: NSMakeRange(0, fileName.characters.count), withTemplate: "$1")
print(extractedName) //test
let mp3Files = ["alarm.mp3", "bubbles.mp3", "fanfare.mp3"]
let ringtonsArray = mp3Files.flatMap { $0.components(separatedBy: ".").first }
You can return a new string removing a definite number of characters from the end.
let fileName = "demoArt.png"
fileName.dropLast(4)
This code returns "demoArt"
One liner:
let stringWithSuffixDropped = fileName.split(separator: ".").dropLast().joined(separator: ".")

Path extractions swift 3.0

I have a file path ...
/acme101/acmeX100/acmeX100.008.png
I can use this to get the extension .png in this case
let leftSide = (lhs.fnName as NSString).pathExtension
And this to get the filename acmeX100
let leftSide = (lhs.fnName as NSString).lastPathComponent
But I want the bit in the middle... the 008 in this case?
Is there a nice one liner?
Assuming the filepath takes that general form then this is (almost) a one-liner (I like to play it safe):
var filePath = "/acme101/acmeX100/acmeX100.008.png"
func extractComponentBetweenDots(inputString: String) -> String? {
guard inputString.components(separatedBy: ".").count > 2 else { print("Incorrect format") ; return nil } // Otherwise not in the correct format, you caa add other tests
return inputString.components(separatedBy: ".")[inputString.components(separatedBy: ".").count - 2]
}
Use as follows:
if let extractedString : String = extractComponentBetweenDots(inputString: filePath) {
print(extractedString)
}
I wanted to make an example using the same technique as in your question - despite the fact that the downcasting to NSString makes the whole thing rather ugly, it works efficiently. This is in Swift 3 but it would be easy to port it back to Swift 2 if needed.
func getComponents(from str: String) -> (name: String, middle: String, ext: String) {
let compo = (str as NSString).lastPathComponent as NSString
let ext = compo.pathExtension
let temp = compo.deletingPathExtension as NSString
let middle = temp.pathExtension
let file = temp.deletingPathExtension
return (name: file, middle: middle, ext: ext)
}
let result = getComponents(from: "/acme101/acmeX100/acmeX100.008.png")
print(result.name) // "acmeX100"
print(result.middle) // "008"
print(result.ext) // "png"
If you only need the middle part:
func pluck(str: String) -> String {
return (((str as NSString).lastPathComponent as NSString).deletingPathExtension as NSString).pathExtension
}
pluck(str: "/acme101/acmeX100/acmeX100.008.png") // "008"
Bon,
Sparky thanks for your answer. I ended up with this .. which is the same and yet different.
func pluck(str:String) -> String {
if !str.isEmpty {
let bitZero = str.characters.split{$0 == "."}.map(String.init)
if (bitZero.count > 2) {
let bitFocus = bitZero[1]
print("bitFocus \(bitFocus)")
return(bitFocus)
}
}
return("")
}

How to create String split extension with regex in Swift?

I wrote extension that create split method:
extension String {
func split(splitter: String) -> Array<String> {
return self.componentsSeparatedByString(splitter)
}
}
So in playground I can write:
var str = "Hello, playground"
if str.split(",").count > 1{
var out = str.split(",")[0]
println("output: \(out)") // output: Hello
}
What do I need to make it work with regex like in Java:
str.split("[ ]+")
Because this way it doesn't work.
Thanks,
First, your split function has some redundancy. It is enough to return
return self.componentsSeparatedByString(splitter)
Second, to work with a regular expression you just have to create a NSRegularExpression and then perhaps replace all occurrences with your own "stop string" and finally separate using that. E.g.
extension String {
func split(regex pattern: String) -> [String] {
let template = "-|*~~*~~*|-" /// Any string that isn't contained in the original string (self).
let regex = try? NSRegularExpression(pattern: pattern)
let modifiedString = regex?.stringByReplacingMatches(
in: self,
range: NSRange(
location: 0,
length: count
),
withTemplate: template /// Replace with the template/stop string.
)
/// Split by the replaced string.
return modifiedString?.components(separatedBy: template) ?? []
}
}