Github username convention using regex - regex

I've been trying to convert the Github username convention using regex in Go for a while now and I couldn't do it. Also the username length shouldn't exceed more than 39 characters.
Below is the username convention from Github
Username may only contain alphanumeric characters or single hyphens, and cannot begin or end with a hyphen.
and for the length
Username is too long (maximum is 39 characters).
Here is the code I've written. You could check here in Go playground
package main
import (
"fmt"
"regexp"
)
func main() {
usernameConvention := "^[a-zA-Z0-9]*[-]?[a-zA-Z0-9]*$"
if re, _ := regexp.Compile(usernameConvention); !re.MatchString("abc-abc") {
fmt.Println("false")
} else {
fmt.Println("true")
}
}
Currently, I could achieve these:
a-b // true - Working!
-ab // false - Working!
ab- // false - Working!
0-0 // true - Working!
But the problem I'm facing is that I couldn't find the regex pattern which should work for the below scenario:
a-b-c // false - Should be true
Also it has to be within 39 characters which I've found that we could use {1,38}, but I don't know where exactly should I add that in the regex pattern.

In Go RE2-based regex, you can't use lookarounds, so checking length limit can only be done either with another regex, or with regular string length checking.
A fully non-regex approach (demo):
package main
import (
"fmt"
"strings"
)
func IsAlnumOrHyphen(s string) bool {
for _, r := range s {
if (r < 'a' || r > 'z') && (r < 'A' || r > 'Z') && (r < '0' || r > '9') && r != '-' {
return false
}
}
return true
}
func main() {
s := "abc-abc-abc"
if len(s) < 40 && len(s) > 0 && !strings.HasPrefix(s, "-") && !strings.Contains(s, "--") && !strings.HasSuffix(s, "-") && IsAlnumOrHyphen(s) {
fmt.Println("true")
} else {
fmt.Println("false")
}
}
Details
len(s) < 40 && len(s) > 0 - Length restriction, from 1 to 39 chars are allowed
!strings.HasPrefix(s, "-") - should not start with -
!strings.Contains(s, "--") - should not contain --
!strings.HasSuffix(s, "-") - should not end with -
IsAlnumOrHyphen(s) - can only contain ASCII alphanumeric and hyphens.
For a partially regex approach, see this Go demo:
package main
import (
"fmt"
"regexp"
)
func main() {
usernameConvention := "^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$"
re,_ := regexp.Compile(usernameConvention)
s := "abc-abc-abc"
if len(s) < 40 && len(s) > 0 && re.MatchString(s) {
fmt.Println("true")
} else {
fmt.Println("false")
}
}
Here, the ^[a-zA-Z0-9]+(?:-[a-zA-Z0-9]+)*$ regex matches
^ - start of string
[a-zA-Z0-9]+ - 1 or more ASCII alphanumeric chars
(?:-[a-zA-Z0-9]+)* - 0 or more repetitions of - and then 1 or more ASCII alphanumeric chars
$ - end of string.

Related

Don't allow consecutive same character and some different character together in TextFormField

I want to restrict the TextFormField to only accept numbers separated by commas and sometimes with dashes but I don't want them to come consecutive to each other and also don't want the same character consecutive.
Ex:-
1,3-4,9-11 is correct
1,,3--4,9-11 is wrong
1,-3-4,9-11 is wrong
1-,3-4,9-11 is wrong
To restrict things to only numbers, commas and dashes I'm using:-
FilteringTextInputFormatter(
RegExp("[0-9,-]"),
allow: true
)
But it is not restricting the consecutive behavior as shown in the wrong behavior in the examples.
So, how can I restrict my TextFormField to the correct behavior represented in the examples?
Thank you.
Update: I finally followed this approach for this problem.
If you want to validate on submit, you might write the pattern as:
^[0-9]+(?:[,-][0-9]+)*$
Regex demo
If a negative lookahead is supported, you an exclude matching 2 times one of - or , while validating on typing.
Note that this will allow , or - at the end:
^(?!.*[,-][,-])[0-9,-]*
Regex demo
For my problem above I finally combined FilteringTextInputFormatter with a custom TextInputFormatter specific to my case so I'm adding it below so that if anyone wants to do the same they can have a look at this approach:
class RangeTextInputFormatter extends TextInputFormatter {
#override
TextEditingValue formatEditUpdate(
TextEditingValue oldValue,
TextEditingValue newValue,
) {
TextSelection newSelection = newValue.selection;
String truncated = newValue.text;
int oldValueLength = oldValue.text.length;
int newValueLength = newValue.text.length;
// Blocks comma and dash at start.
if ((oldValue.text.isEmpty || oldValue.text == "") &&
(newValue.text[newValueLength - 1] == "," ||
newValue.text[newValueLength - 1] == "-")) {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
// Allows numbers at start.
else if (oldValue.text.isEmpty || oldValue.text == "") {
truncated = newValue.text;
newSelection = newValue.selection;
} else {
// Blocks comma and dash after comma.
if (oldValue.text[oldValueLength - 1] == "," &&
(newValue.text[newValueLength - 1] == "," ||
newValue.text[newValueLength - 1] == "-")) {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
// Blocks comma and dash after dash.
else if (oldValue.text[oldValueLength - 1] == "-" &&
(newValue.text[newValueLength - 1] == "," ||
newValue.text[newValueLength - 1] == "-")) {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
// Blocks dash after number dash number. Ex: 48-58- <- this last dash is blocked
else if (oldValue.text.lastIndexOf('-') != -1) {
if (!(oldValue.text
.substring(oldValue.text.lastIndexOf('-'))
.contains(",")) &&
newValue.text[newValueLength - 1] == "-") {
truncated = oldValue.text;
newSelection = oldValue.selection;
}
}
}
return TextEditingValue(
text: truncated,
selection: newSelection,
composing: TextRange.empty,
);
}
}
Now use it just like FilteringTextInputFormatter:
inputFormatters: [
FilteringTextInputFormatter(RegExp("[0-9,-]"), allow: true),
RangeTextInputFormatter(),
]

Regular Expression for conditional replacement of parts of string in US phone number mask (Swift compatible)

I try to come up with regular expression patter that fulfills such requirements.
it is US phone number format wit 3 groups
I have input strings like this
(999) 98__-9999 here there is extra _ at the end of second section which I want to delete
(999) 9_8_-9999 here there is extra _ at the end of second section I want to delete
(999) 9_-9999 here if second group length is < 3 and ends with _ there should be added _ to pad second group to 9__ (3 characters)
(999) 98-9999 here if second group length is equal to 3 or it ends with digit there shouldn't be any modifications
To sum up:
If secondGroup.length > 3 && secondGroup.lastCharacter == '_' I want to remove this last character
else if secondGroup.length < 3 && secondGroup.lastCharacter == '_' I wan to append "_" (or pad wit underscore to have 3 characters in total)
else leave second group as in the input string.
The same should be applied to first group. The difference are the different delimiters i.e. (xxx) in first group and \sxxx- in second group
Here is my Swift code I have used to achieve it in brute force way by manually manipulating the string: (length 4 instead of 3 takes into account first delimiter like ( or \s. )
var componentText = ""
let idx1 = newText.index(of: "(")
let idx2 = newText.index(of: ")")
if let idx1 = idx1, let idx2 = idx2 {
var component0 = newText[..<idx1]
var component1 = newText[idx1..<idx2]
if component1.count > 4 && component1.last == "_" {
component1.popLast()
} else if component1.count < 4 && component1.last == "_" {
component1.append("_")
}
componentText += "\(component0)\(component1))"
} else {
componentText = newText
}
let idx3 = newText.index(of: " ")
let idx4 = newText.index(of: "-")
if let idx2 = idx2, let idx3 = idx3, let idx4 = idx4 {
var component2 = newText[idx2..<idx3]
component2.popFirst()
var component3 = newText[idx3..<idx4]
var component4 = newText[idx4...]
if component3.count > 4 && component3.last == "_" {
component3.popLast()
} else if component3.count < 4 && component3.last == "_" {
component3.append("_")
}
componentText += "\(component2) \(component3)-\(component4)"
} else {
componentText = newText
}
newText = componentText != "" ? componentText : newText
I think that using regular expression this code could be more flexible and much shorter.

Regex: Any letters, digit, and 0 up to 3 special chars

It seems I'm stuck with a simple regex for a password check.
What I'd like:
8 up to 30 symbols (Total)
With any of these: [A-Za-z\d]
And 0 up to 3 of these: [ -/:-#[-`{-~À-ÿ] (Special list)
I took a look here and then I wrote something like:
(?=.{8,15}$)(?=.*[A-Za-z\d])(?!([ -\/:-#[-`{-~À-ÿ])\1{4}).*
But it doesn't work, one can put more than 3 of the special chars list.
Any tips?
After shuffling your regex around a bit, it works for the examples you provided (I think you made a mistake with the example "A#~` C:", it should not match as it has 6 special chars):
(?!.*(?:[ -\/:-#[-`{-~À-ÿ].*){4})^[A-Za-z\d -\/:-#[-`{-~À-ÿ]{8,30}$
It only needs one lookahead instead of two, because the length and character set check can be done without lookahead: ^[A-Za-z\d -/:-#[-`{-~À-ÿ]{8,30}$
I changed the negative lookahead a bit to be correct. Your mistake was to only check for consecutive special chars, and you inserted the wildcards .* in a way that made the lookahead never hit (because the wildcard allowed everything).
Will this work?
string characters = " -/:-#[-`{-~À-ÿ";
string letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
string[] inputs = {
"AABBCCDD",
"aaaaaaaa",
"11111111",
"a1a1a1a1",
"AA####AA",
"A1C EKFE",
"AADE F"
};
foreach (string input in inputs)
{
var counts = input.Cast<char>().Select(x => new { ch = characters.Contains(x.ToString()) ? 1 : 0, letter = letters.Contains(x.ToString()) ? 1 : 0, notmatch = (characters + letters).Contains(x) ? 0 : 1}).ToArray();
Boolean isMatch = (input.Length >= 8) && (input.Length <= 30) && (counts.Sum(x => x.notmatch) == 0) && (counts.Sum(x => x.ch) <= 3);
Console.WriteLine("Input : '{0}', Matches : '{1}'", input, isMatch ? "Match" : "No Match");
}
Console.ReadLine();
I would use: (if you want to stick to Regex)
var specialChars = #" -\/:-#[-`{-~À-ÿ";
var regularChars = #"A-Za-z\d";
if (Regex.Match(password,$"^(.[{regularChars}{specialChars}]{7,29})$").Success && Regex.Matches(password, $"[{specialChars}]").Count<=3))
{
//Password OK
}
If consists of:
Check Length and if password contains illegal characters
Check if ony contains 3 times special char
A litle faster:
var specialChars = #" -\/:-#[-`{-~À-ÿ";
var regularChars = #"A-Za-z\d";
var minChars = 8;
var maxChars = 30;
if (password.Length >= minChars && password.Length <= maxChars && Regex.Match(password,$"^[{regularChars}{specialChars}]+$").Success && Regex.Matches(password, $"[{specialChars}]").Count<=3))
{
//Password OK
}
Newbie here..I think I've managed to get what you need but one of the test cases you shared was kinda weird..
A#~` C:
OK -- Match (3 specials, it's okay)
Shouldn't this be failed because it has more than 3 specials?
Could you perhaps try this? If it works I'll type out the explanations for the regex.
https://regex101.com/r/KCL6R1/2
(?=^[A-Za-z\d -\/:-#[-`{-~À-ÿ]{8,30}$)^(?:[A-Za-z\d]*[ -\/:-#[-`{-~À-ÿ]){0,3}[A-Za-z\d]*$

PHP regex pattern for password checking not working in Go

After reading this post, I know I need to use a backquotes ( ` ) to wrap my regex pattern. Now I have regex pattern /^(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s)[0-9a-zA-Z!##$%^&*()]*$/ to check password pattern whether is correct. I have tested it with PHP and it work correctly. But it does not work in Go. Why?
By the way, what is the type of backquoted ( ` ) variable? it seems not string type. How can I declare this variable container?
Testing Code
package main
import(
"fmt"
"regexp"
)
func main(){
re := regexp.MustCompile(`/^(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s)[0-9a-zA-Z!##$%^&*()]*$/`)
fmt.Println(re.MatchString("aSfd46Fgwaq"))
}
Testing Result
Running...
panic: regexp: Compile(`/^(?=^.{8,}$)(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s)[0-9a-zA-Z!##$%^&*()]*$/`): error parsing regexp: invalid or unsupported Perl syntax: `(?=`
goroutine 1 [running]:
panic(0x4efae0, 0xc82000a340)
/usr/local/go/src/runtime/panic.go:481 +0x3e6
regexp.MustCompile(0x576140, 0x4b, 0x100000000)
/usr/local/go/src/regexp/regexp.go:232 +0x16f
main.main()
/home/casper/.local/share/data/liteide/goplay.go:9 +0x30
exit status 2
Error: process exited with code 1.
Thank you!
Go regexp does not support lookarounds. Also, the /.../ regex delimiters are not supported, either (there are special methods in Go to express those).
You may either use a lookaround-supporting regex library for Go (here is a PCRE-supporting one) or split the conditions and use several small, readable regexps:
package main
import (
"fmt"
"regexp"
"unicode/utf8"
)
func main() {
s := "aSfd46Fgwaq"
lower_cond := regexp.MustCompile(`[a-z]`)
upper_cond := regexp.MustCompile(`[A-Z]`)
digit_cond := regexp.MustCompile(`[0-9]`)
whole_cond := regexp.MustCompile(`^[0-9a-zA-Z!##$%^&*()]*$`)
pass_len := utf8.RuneCountInString(s)
fmt.Println(lower_cond.MatchString(s) && upper_cond.MatchString(s) && digit_cond.MatchString(s) && whole_cond.MatchString(s) && pass_len >= 8)
}
See Go playground demo
NOTE: I used utf8.RuneCountInString in the demo to make sure even UTF8 string length is parsed correctly. Otherwise, you might use len(s) that will count bytes that should suffice for ASCII input.
Update
If the performance is not satisfactory, you may consider using a simpler non-regex approach:
package main
import "fmt"
func main() {
myString := "aSfd46Fgwaq"
has_digit := false
has_upper := false
has_lower := false
pass_length := len(myString)
for _, value := range myString {
switch {
case value >= '0' && value <= '9':
has_digit = true
case value >= 'A' && value <= 'Z':
has_upper = true
case value >= 'a' && value <= 'z':
has_lower = true
}
}
fmt.Println(has_digit && has_upper && has_lower && pass_length >= 8)
}
See another Go demo

How do I include the operators in my output when I split my string?

Yesterday I asked this question about splitting a string in python. I've since decided to do this project in Go instead. I have the following:
input := "house-width + 3 - y ^ (5 * house length)"
s := regexp.MustCompile(" ([+-/*^]) ").Split(input, -1)
log.Println(s) // [house-width 3 y (5 house length)]
How do I include the operators in this output? e.g. I'd like the following output:
['house-width', '+', '3', '-', 'y', '^', '(5', '*', 'house length)']
EDIT:
To clarify I am splitting on the space-separated operators and not just the operator. The operator must have a space on both ends to differentiate it from a dash/hyphen. Please refer to my original python question I linked to for clarification if needed.
You can get the operands of your expression using regexp.Split() (just as you did) and you can get the operators (the separators) using regexp.FindAllString().
By doing this you will have 2 separate []string slices, you can merge these 2 slices if you want the result in one []string slice.
input := "house-width + 3 - y ^ (5 * house length)"
r := regexp.MustCompile(`\s([+\-/*^])\s`)
s1 := r.Split(input, -1)
s2 := r.FindAllString(input, -1)
fmt.Printf("%q\n", s1)
fmt.Printf("%q\n", s2)
all := make([]string, len(s1)+len(s2))
for i := range s1 {
all[i*2] = s1[i]
if i < len(s2) {
all[i*2+1] = s2[i]
}
}
fmt.Printf("%q\n", all)
Output (try it on the Go Playground):
["house-width" "3" "y" "(5" "house length)"]
[" + " " - " " ^ " " * "]
["house-width" " + " "3" " - " "y" " ^ " "(5" " * " "house length)"]
Note:
If you want to trim the spaces from the operators, you can use the strings.TrimSpace() function for that:
for i, v := range s2 {
all[i*2+1] = strings.TrimSpace(v)
}
fmt.Printf("%q\n", all)
Output:
["house-width" "+" "3" "-" "y" "^" "(5" "*" "house length)"]
If you're planing to parse the expression afterwards you'll have to make some changes:
Include parentheses as lexemes
You can't have both spaces and dashes be valid identifier characters because e.g. - y inbetween 3 and ^ would be a valid identifier.
After that's done, you can use use a simple linear iteration to lex your string:
package main
import (
"bytes"
"fmt"
)
func main() {
input := `house width + 3 - y ^ (5 * house length)`
buffr := bytes.NewBuffer(nil)
outpt := make([]string, 0)
for _, r := range input {
if r == '+' || r == '-' || r == '*' || r == '/' || r == '^' || r == '(' || r == ')' || (r >= '0' && r <= '9') {
bs := bytes.TrimSpace(buffr.Bytes())
if len(bs) > 0 {
outpt = append(outpt, (string)(bs))
}
outpt = append(outpt, (string)(r))
buffr.Reset()
} else {
buffr.WriteRune(r)
}
}
fmt.Printf("%#v\n", outpt)
}
Once lexed, use Dijkstra's shunting-yard algorithm to build an AST or directly evaluate the expression.
I think FindAll() may be the way to go.
Expanded regex:
\s* # Trim preceding whitespace
( # (1 start), Operator/Non-Operator chars
(?: # Cluster group
\w - # word dash
| - \w # or, dash word
| [^+\-/*^] # or, a non-operator char
)+ # End cluster, do 1 to many times
| # or,
[+\-/*^] # A single simple math operator
) # (1 end)
\s* # Trim trailing whitespace
Go code snippet:
http://play.golang.org/p/bHZ21B6Tzi
package main
import (
"log"
"regexp"
)
func main() {
in := []byte("house-width + 3 - y ^ (5 * house length)")
rr := regexp.MustCompile("\\s*((?:\\w-|-\\w|[^+\\-/*^])+|[+\\-/*^])\\s*")
s := r.FindAll( in, -1 )
for _, ss:=range s{
log.Println(string(ss))
}
}
Output:
house-width
+
3
-
y
^
(5
*
house length)