2

Hello my Friends i need ur help! :) Im an absolute beginner in swift and have absolutely no idea what i'm doing.

I need to extract all numbers from a txt file which i converted into a string. I want to save all the numbers into a new Array and i also have to use CharacterView to solve the problem.

This is what i got so far. Reading from the txt file is no problem.

func readFile(){
    let path = Bundle.main.path(forResource: "paragraph", ofType: "txt")

    var fileContents : String? = nil
    do {
        fileContents = try String(contentsOfFile: path!, encoding: String.Encoding.utf8)
    } catch _ as NSError {
        //ERROR HANDLING
    }
     print(fileContents)

}

this is the file:

§ 9 Lorem ipsum dolor (1) sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam. (2) Bed diam voluptua. At vero eos et accusam et justo duo 55 dolores et ea rebum. Stet clita kasd gubergren. (3) 1 abore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd.

Thank you for your time!

Tabax
  • 43
  • 1
  • 6

2 Answers2

5

Details

  • Xcode 10.2.1 (10E1001), Swift 5

Solution

import Foundation

extension String {
    func onlyNumbers() -> [NSNumber] {
        let formatter = NumberFormatter()
        formatter.numberStyle = .decimal
        let charset = CharacterSet.init(charactersIn: " ,")
        return matches(for: "[+-]?([0-9]+([., ][0-9]*)*|[.][0-9]+)").compactMap { string in
            return formatter.number(from: string.trimmingCharacters(in: charset))
        }
    }

    // https://stackoverflow.com/a/54900097/4488252
    func matches(for regex: String) -> [String] {
        guard let regex = try? NSRegularExpression(pattern: regex, options: [.caseInsensitive]) else { return [] }
        let matches  = regex.matches(in: self, options: [], range: NSMakeRange(0, self.count))
        return matches.compactMap { match in
            guard let range = Range(match.range, in: self) else { return nil }
            return String(self[range])
        }
    }
}

Usage

let text = "dasdxcw 12345678 eadwd 333.222 reqw 3 333 444.22, sfsa 44. qewqwdsaw qe aw, 0.123456678 and .23 asdasdas 32,322,222,444.8"
print("Original text: \(text)")
print("Only numbers: \(text.onlyNumbers())")

Results

//Original text: dasdxcw 12345678 eadwd 333.222 reqw 3 333 444.22, sfsa 44. qewqwdsaw qe aw, 0.123456678 and .23 asdasdas 32,322,222,444.8
//Only numbers: [12345678, 333.222, 3333444.22, 44, 0.123456678, 0.23, 32322222444.8]
Vasily Bodnarchuk
  • 24,482
  • 9
  • 132
  • 127
2

Here's an easy way to do it, unfortunately it does not use CharacterView because that would complicate it greatly:

func readFile(){
  // Make sure getting the path and reading the file succeeds
  guard
    let path = Bundle.main.path(forResource: "paragraph", ofType: "txt"),
    let fileContents = try? String(contentsOfFile: path, encoding: String.Encoding.utf8) 
    else { return }

  // split string into substrings that only contain numbers
  let substrings = fileContents.components(separatedBy: CharacterSet.decimalDigits.inverted)

  // flatMap performs action on each substring
  // and only returns non-nil values,
  // thus returns [Int]

  let numbers = substrings.flatMap {
    // convert each substring into an Int?
    return Int($0)
  }

  print(numbers)
}

Because the initializer for Int takes a String there is no need to use CharacterView. Once the numbers in the text are split from their non-digits they can be converted directly from String to Int. To use CharacterView would be an unnecessary intermediate. You could, however, code your own version of Int init?(String) which uses CharacterView to build the value.

  • Note that I didn't need the variable `substrings`, I could have skipped it and done `fileContents.components(separatedBy: CharacterSet.decimalDigits.inverted).flatMap { return Int($0) }`. I separated it to demonstrate the steps involved. –  Jul 26 '17 at 15:27