This question already has answers here:
Match exact string
(3 answers)
Closed 6 years ago.
I am working on a username regex where the only characters that are accepted are a-z, A-Z, 0-9, and _. The current max length of the username is 18 characters with a minimum of one. My current regex is below.
let regexCorrectPattern = "[a-zA-Z0-9_]{1,18}$"
I am having the following issue. Special characters added anywhere but the end of the string allow the regex to pass. For example
jon! FAIL
!jon PASS
j!on PASS
The method I am using to test the regex is below along with the calling method. Any input would be greatly appreciated.
Regex Testing Method
func regexTestString(string: String, withPattern regexPattern: String) -> Bool
{
// This method is used for all of the methods below.
do
{
// Create regex and regex range.
let regex = try NSRegularExpression(pattern: regexPattern, options: .CaseInsensitive)
let range = NSMakeRange(0, string.characters.count)
// Test for the number of regex matches.
let numberOfMatches = regex.numberOfMatchesInString(string, options: [], range: range)
// Testing Code.
print(numberOfMatches)
// Return true if the number of matches is greater than 1 and return false if the number of mathces is 0.
return (numberOfMatches == 0) ? false : true
}
catch
{
// Testing Code
print("There is an error in the SignUpViewController regexTestString() method \(error)")
// If there is an error return false.
return false
}
}
Calling Method
func usernameTextFieldDidEndEditing(sender: AnyObject)
{
let usernameText = self.usernameField.text!.lowercaseString
let regexCorrectPattern = "[a-zA-Z0-9_]{1,18}$"
let regexWhitespacePattern = "\\s"
let regexSpecialCharacterPattern = ".*[^A-Za-z0-9].*"
if regexTestString(usernameText, withPattern: regexCorrectPattern)
{
// The regex has passed hide the regexNotificationView
}
else if regexTestString(usernameText, withPattern: regexWhitespacePattern)
{
// The username contains whitespace characters. Alert the user.
}
else if regexTestString(usernameText, withPattern: regexSpecialCharacterPattern)
{
// The username contains special characters. Alert the user.
}
else if usernameText == ""
{
// The usernameField is empty. Make sure the sign up button is disabled.
}
else
{
// For some reason the Regex is false. Disable the sign up button.
}
}
You want the entire string to contain only the characters you specified, so all you need is to add ^ at the start of the pattern:
^[a-zA-Z0-9_]{1,18}$
Related
Goal:
I have an answer box that will take inputs from a user. To reduce the amount of possible correct answers I enter into the backend I would like to use a RegExp to ignore certain words/characters at the start and at the end of the answer.
So if the answer was: mountain.
I would also accept: the mountain OR a mountain OR the mountains...
What I have tried:
bool checkAnswer(String answer) {
List _answers = widget.answers;
for (var i = 0; i < _answers.length; ++i) {
// This
_correctAnswer = RegExp("?!(the)|(an)|(a)?${widget.answers[i]}s?").hasMatch(answer);
if (_correctAnswer) {
return _correctAnswer;
}
}
return false;
}
This function looks at the user input and compares it to the answers I've inputted on the backend. The problem is that it also accepts individual characters 't,h,e'...etc
So you want to remove a leading "the " or "a ", and a trailing plural "s".
Maybe:
bool checkAnswer(String answer, List<String> validAnswers) {
for (var i = 0; i < validAnswers.length; i++) {
var re = RegExp("^(?:the | an? )?${validAnswers[i]}s?\$", caseSensitive: false);
if (re.hasMatch(answer)) return true;
}
return false;
}
This checks that string is one of the valid answers, optionally prefixed by the , a or an , and optionally suffixed by s.
The leading ^ and trailing $ (\$ because it's in a non-raw string literal) ensures that the regexp matches the entire input (the ^ matches the beginning of the string and $ matches the end of the string). The | matches either the thing before it or the thing after it (the "alternatives").
If you want to match more different suffixes, say "es" or ".", you change the s? to accept those too:
var re = RegExp("^(?:the | an? )?${validAnswers[i]}(?:e?s)?\\.?\$", caseSensitive: false);
I'm guessing you want to accept s or es and, independently of that, a ..
Your original RegExp was not valid. If it runs at all, I cannot predict what it would do. (For example, a RegExp must not start with ?).
I recommend reading a RegExp tutorial.
This is how I ended solving the problem to :
Ignore at the start: the, an, a
Ignore at the end: s, es and .
bool checkAnswer(String answer) {
List _answers = widget.answers;
for (var i = 0; i < _answers.length; i++) {
var re = RegExp("^(?:the |an |a )?${_answers[i]}(?:s|es)?.?\$", caseSensitive: false);
if (re.hasMatch(answer)) return true;
}
return false;
}
I want to choose a password for the user to have at least 8 characters in the password he chooses, and also the number of letters, and that the duplicate numbers should not be sequential numbers when they want to write the password.
This called NSRegularExpression, in the first you need to declare the regular expression string like this:
let langRexEx = "^[a-z.]+$"
Then create a function to check the string with the regular expression. For example just English chars and dot:
func verifyLanguage(value: String) -> Bool {
var returnValue = true
let langRexEx = "^[a-z.]+$" // just chars and dot
do {
let regex = try NSRegularExpression(pattern: langRexEx)
let nsString = value as NSString
let results = regex.matches(in: value, range: NSRange(location: 0, length: nsString.length))
if results.count == 0
{
returnValue = false
}
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
returnValue = false
}
return returnValue
}
Important note: string of regular is hard to find.
My function should handle every regex and return a true or false. It's working good... still now
func test(_ input: String) -> Bool {
let pattern = ".{7}" //allow exactly 7 numbers
let regex = try! NSRegularExpression(pattern: pattern, options: [NSRegularExpression.Options.caseInsensitive])
let leftover = regex.stringByReplacingMatches(in: input, options: [], range: NSMakeRange(0, input.characters.count), withTemplate: "")
if leftover.isEmpty {
return true
}
return false
}
print(test("123456")) //false
print(test("1234567")) //true
print(test("12345678")) //false
print(test("")) //true - I expect false
So I understand why test("") is false. But how can I fix my regex that it return false?
Sometimes I use the regex .* My function should handle this one, too. So I can't make a check like this
if input.isEmpty {
return false
}
If input is the empty string then leftover will be the empty string
as well, and therefore your function returns true. Another case where
your approach fails is
print(test("12345671234567")) // true (expected: false)
An alternative is to use the range(of:) method of String with the .regularExpression option. Then check if the matched range is the entire string.
In order to match 7 digits (and not 7 arbitrary characters), the
pattern should be \d{7}.
func test(_ input: String) -> Bool {
let pattern = "\\d{7}"
return input.range(of: pattern, options: [.regularExpression, .caseInsensitive])
== input.startIndex..<input.endIndex
}
A solution is to specify that your regex has to match the entire string to be valid, so you can do this by adding ^ and $ at your regex to ensure the start and the end of the string.
let pattern = "^.{7}$" //allow exactly 7 numbers
let regex = try! NSRegularExpression(pattern: pattern, options: [.caseInsensitive])
let numberOfOccurences = regex.numberOfMatches(in: input, options: [], range: NSMakeRange(0, input.utf16.count))
return (numberOfOccurences != 0)
In theory, we should be checking if numberOfOccurences is truly equal to 1 to return true, but checking the start and the end should give you only one or zero match.
I want to convert an uppercase string (UPPERCASE) into a title case string (Title Case) in swift. I am not strong in regular expressions, but have found this answer with a regular expression that I have attempted to use.
The search expression is:
"([A-Z])([A-Z]+)\b"
and the template expression is:
"$1\L$2"
In order to use it in swift I have escaped the backslashes as seen below:
var uppercase = "UPPER CASE STRING"
var titlecase = uppercase.stringByReplacingOccurrencesOfString("([A-Z])([A-Z]+)\\b", withString: "$1\\L$2", options: NSStringCompareOptions.RegularExpressionSearch, range: Range<String.Index>(start: uppercase.startIndex, end: uppercase.endIndex))
The code above gives the following result:
"ULPPER CLASE SLTRING"
From that you can see that the search expression successfully finds the two parts $1 and $2, but it looks like escaping the backslash interferes with the replacement.
How can I get the expected result of:
"Upper Case String"
Many of the useful existing NSString methods are available from Swift. This includes capitalizedString, which may just do exactly what you want, depending on your requirements.
As I know, title cased string is the string that has the first letter of each word capitalised (except for prepositions, articles and conjunctions). So, the code should be like that:
public extension String {
subscript(range: NSRange) -> Substring {
get {
if range.location == NSNotFound {
return ""
} else {
let swiftRange = Range(range, in: self)!
return self[swiftRange]
}
}
}
/// Title-cased string is a string that has the first letter of each word capitalised (except for prepositions, articles and conjunctions)
var localizedTitleCasedString: String {
var newStr: String = ""
// create linguistic tagger
let tagger = NSLinguisticTagger(tagSchemes: [.lexicalClass], options: 0)
let range = NSRange(location: 0, length: self.utf16.count)
tagger.string = self
// enumerate linguistic tags in string
tagger.enumerateTags(in: range, unit: .word, scheme: .lexicalClass, options: []) { tag, tokenRange, _ in
let word = self[tokenRange]
guard let tag = tag else {
newStr.append(contentsOf: word)
return
}
// conjunctions, prepositions and articles should remain lowercased
if tag == .conjunction || tag == .preposition || tag == .determiner {
newStr.append(contentsOf: word.localizedLowercase)
} else {
// any other words should be capitalized
newStr.append(contentsOf: word.localizedCapitalized)
}
}
return newStr
}
}
I am making an app in Swift and I need to catch 8 numbers from a string.
Here's the string:
index.php?page=index&l=99182677
My pattern is:
&l=(\d{8,})
And here's my code:
var yourAccountNumber = "index.php?page=index&l=99182677"
let regex = try! NSRegularExpression(pattern: "&l=(\\d{8,})", options: NSRegularExpressionOptions.CaseInsensitive)
let range = NSMakeRange(0, yourAccountNumber.characters.count)
let match = regex.matchesInString(yourAccountNumber, options: NSMatchingOptions.Anchored, range: range)
Firstly, I don't know what the NSMatchingOptions means, on the official Apple library, I don't get all the .Anchored, .ReportProgress, etc stuff. Anyone would be able to lighten me up on this?
Then, when I print(match), nothing seems to contain on that variable ([]).
I am using Xcode 7 Beta 3, with Swift 2.0.
ORIGINAL ANSWER
Here is a function you can leverage to get captured group texts:
import Foundation
extension String {
func firstMatchIn(string: NSString!, atRangeIndex: Int!) -> String {
var error : NSError?
let re = NSRegularExpression(pattern: self, options: .CaseInsensitive, error: &error)
let match = re.firstMatchInString(string, options: .WithoutAnchoringBounds, range: NSMakeRange(0, string.length))
return string.substringWithRange(match.rangeAtIndex(atRangeIndex))
}
}
And then:
var result = "&l=(\\d{8,})".firstMatchIn(yourAccountNumber, atRangeIndex: 1)
The 1 in atRangeIndex: 1 will extract the text captured by (\d{8,}) capture group.
NOTE1: If you plan to extract 8, and only 8 digits after &l=, you do not need the , in the limiting quantifier, as {8,} means 8 or more. Change to {8} if you plan to capture just 8 digits.
NOTE2: NSMatchingAnchored is something you would like to avoid if your expected result is not at the beginning of a search range. See documentation:
Specifies that matches are limited to those at the start of the search range.
NOTE3: Speaking about "simplest" things, I'd advise to avoid using look-arounds whenever you do not have to. Look-arounds usually come at some cost to performance, and if you are not going to capture overlapping text, I'd recommend to use capture groups.
UPDATE FOR SWIFT 2
I have come up with a function that will return all matches with all capturing groups (similar to preg_match_all in PHP). Here is a way to use it for your scenario:
func regMatchGroup(regex: String, text: String) -> [[String]] {
do {
var resultsFinal = [[String]]()
let regex = try NSRegularExpression(pattern: regex, options: [])
let nsString = text as NSString
let results = regex.matchesInString(text,
options: [], range: NSMakeRange(0, nsString.length))
for result in results {
var internalString = [String]()
for var i = 0; i < result.numberOfRanges; ++i{
internalString.append(nsString.substringWithRange(result.rangeAtIndex(i)))
}
resultsFinal.append(internalString)
}
return resultsFinal
} catch let error as NSError {
print("invalid regex: \(error.localizedDescription)")
return [[]]
}
}
// USAGE:
let yourAccountNumber = "index.php?page=index&l=99182677"
let matches = regMatchGroup("&l=(\\d{8,})", text: yourAccountNumber)
if (matches.count > 0) // If we have matches....
{
print(matches[0][1]) // Print the first one, Group 1.
}
It may be easier just to use the NSString method instead of NSRegularExpression.
var yourAccountNumber = "index.php?page=index&l=99182677"
println(yourAccountNumber) // index.php?page=index&l=99182677
let regexString = "(?<=&l=)\\d{8,}+"
let options :NSStringCompareOptions = .RegularExpressionSearch | .CaseInsensitiveSearch
if let range = yourAccountNumber.rangeOfString(regexString, options:options) {
let digits = yourAccountNumber.substringWithRange(range)
println("digits: \(digits)")
}
else {
print("Match not found")
}
The (?<=&l=) means precedes but not part of.
In detail:
Look-behind assertion. True if the parenthesized pattern matches text preceding the current input position, with the last character of the match being the input character just before the current position. Does not alter the input position. The length of possible strings matched by the look-behind pattern must not be unbounded (no * or + operators.)
In general performance considerations of a look-behind without instrumented proof is just premature optimization. That being said there may be other valid reasons for and against look-arounds in regular expressions.
ICU User Guide: Regular Expressions
For Swift 2, you can use this extension of String:
import Foundation
extension String {
func firstMatchIn(string: NSString!, atRangeIndex: Int!) -> String {
do {
let re = try NSRegularExpression(pattern: self, options: NSRegularExpressionOptions.CaseInsensitive)
let match = re.firstMatchInString(string as String, options: .WithoutAnchoringBounds, range: NSMakeRange(0, string.length))
return string.substringWithRange(match!.rangeAtIndex(atRangeIndex))
} catch {
return ""
}
}
}
You can get the account-number with:
var result = "&l=(\\d{8,})".firstMatchIn(yourAccountNumber, atRangeIndex: 1)
Replace NSMatchingOptions.Anchored with NSMatchingOptions() (no options)