0

I need some code to make sure that if a whole word exists in a return formatted text file it is accepted and that, if only part of it is present, it is not considered.

If I type lau in the TextField it is accepted and I would rather the answer was false until a whole word is matched

Here is the file limited.txt I use in my project. Each word is on a separate line: appetitive appetitiveness appetitost appetize appetized appetizement appetizer appetizers appetizing appetizingly appinite appius appl applanate applanation applaud applaudable applaudably applauded applauder applauders applauding applaudingly applauds applause applauses applausive applausively apple appleberry appleblossom applecart appled appledrane appledrone applegrower applejack applejohn applemonger

Thanks for your help

import SwiftUI

struct ContentView: View{
    @ObservedObject var textFileStringContent: TexFileReader
    @State private var text = ""

    var body: some View{
        VStack {
            TextField("please type the word to check", text: $text)
            // so that it does not matter if user capitalises a word
            if textFileStringContent.data.contains(self.text.lowercased()) {
                Text("part of it exists")
                
                // I tried to code it in here but to no avail
                // if it is a whole word {
                // Text("congratulations it does exist")
                // }
                
            } else if !text.isEmpty {
                Text("sorry no such word")
            }
        }.padding().font(.headline)
            .navigationBarTitle("Word checker")
    }
}

class TexFileReader: ObservableObject {
    @Published var data: String = ""
    
    init() { self.load(file: "limited") }
    func load(file: String) {
        if let filepath = Bundle.main.path(forResource: file, ofType: "txt") {
            do {
                let contents = try String(contentsOfFile: filepath)
                DispatchQueue.main.async {
                    self.data = contents
                    
                     print(self.data.contains("lau"))
                    // this prints true even if lau is not a whole word
                    // applaud
                    // applaudable
                    // applaudably
                    // applauded
                    // applauder
                    // applauders
                    // applauding
                    // applaudingly
                    // applauds
                    // applause
                    // applauses
                    // applausive
                    // applausively
                    // but present in each of these
                    // I need to make sure that the match is a whole word not just part of one
                   
                    
                }
            } catch let error as NSError {
                print(error.localizedDescription)
            }
        } else {
            print("File not found")
        }
    }
}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thank you to all three. It was nice to wake up this morning to find help. I implemented Vadian's solution first and it worked perfectly so I never looked into the other two. Have a nice day all of you. – Mariano Giovanni Mario Leonti Aug 06 '20 at 04:29

3 Answers3

1

A possible way is to search with Regular Expression and the word boundary specifier \\b

if textFileStringContent.data.range(of: "\\b\(self.text)\\b", options: [.caseInsensitive, .regularExpression]) != nil {
vadian
  • 274,689
  • 30
  • 353
  • 361
0

You may check if it ends with a newline separator in your text file:

let textWithNewline = self.text.lowercased() + "\n"
if textFileStringContent.data.contains(textWithNewline) {
    // it is a whole word
}
pawello2222
  • 46,897
  • 22
  • 145
  • 209
  • Or we can trim new line and space for both texts before compare. `trimmingCharacters(in: .whitespacesAndNewlines)` – Canh Tran Aug 05 '20 at 11:10
0

Foundation contains a language analysis engine NSLinguisticTagger which can do many things including finding specific words with locale sensitivity.

A simple implementation of what you're trying to do is:

//extension via https://stackoverflow.com/questions/15062458/shortcut-to-generate-an-nsrange-for-entire-length-of-nsstring/56391610#56391610
extension String {
    func range(from nsRange: NSRange) -> Range<String.Index>? {
        return Range(nsRange, in: self)
    }
}

var tagger = NSLinguisticTagger(tagSchemes: [NSLinguisticTagScheme.tokenType], options: 0)

let baddata = """
applaud
applaudable
applaudably
applauded
applauder
applauders catlau
applauding
"""

let gooddata = """
applaud
applaudable
applaudably
applauded
applauder
applauders lau catlau
applauding
"""

var foundLau = false
tagger.string = baddata
tagger.enumerateTags(in: NSRange(location: 0, length: baddata.count), scheme: .tokenType, options: [.omitWhitespace]) { tag, tokenRange, _, _ in
    if tag != nil, let range = baddata.range(from: tokenRange) {
        let fragment = baddata[range]
        if fragment.lowercased() == "lau" {
            foundLau = true
        }
    }
}

print("found \"lau\" in baddata =", foundLau ? "true":"false")

tagger.string = gooddata
tagger.enumerateTags(in: NSRange(location: 0, length: gooddata.count), scheme: .tokenType, options: [.omitWhitespace]) { tag, tokenRange, _, _ in
    if tag != nil, let range = gooddata.range(from: tokenRange) {
        let fragment = gooddata[range]
        if fragment.lowercased() == "lau" {
            foundLau = true
        }
    }
}

print("found \"lau\" in gooddata =", foundLau ? "true":"false")

enumerateTags returns an NSRange which can be converted to Range for general Swift use.

Warren Burton
  • 17,451
  • 3
  • 53
  • 73
  • Never create `NSRange` from a Swift String with `NSRange(location:length:)`. There is a convenient API. Please see https://stackoverflow.com/questions/15062458/shortcut-to-generate-an-nsrange-for-entire-length-of-nsstring/56391610#56391610. The NSString bridge cast isn't needed either. – vadian Aug 05 '20 at 11:44