Keypress ISSUE VB.NET - regex

I took many hours trying to solve this problem I have attempted, without success.
All I need is to validate a textbox:
Valid Chains:
10%
0%
1111111.12%
15.2%
10
2.3
Invalid Chains:
.%
12.%
.02%
%
123456789123.123
I need to validate the textbox with these valid chains, supporting the keypress event.
I tryed:
Private Sub prices_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles wholeprice_input_new_item.KeyPress, dozenprice_input_new_item.KeyPress, _
detailprice_input_new_item.KeyPress, costprice_input_new_item.KeyPress
Dim TxtB As TextBox = CType(sender, TextBox)
Dim fullText As String = TxtB.Text & e.KeyChar
Dim rex As Regex = New Regex("^[0-9]{1,9}([\.][0-9]{1,2})?[\%]?$ ")
If (Char.IsDigit(e.KeyChar) Or e.KeyChar.ToString() = "." Or e.KeyChar = CChar(ChrW(Keys.Back))) Then
If (fullText.Trim() <> "") Then
If (rex.IsMatch(fullText) = False And e.KeyChar <> CChar(ChrW(Keys.Back))) Then
e.Handled = True
MessageBox.Show("You are Not Allowed To Enter More then 2 Decimal!!")
End If
End If
Else
e.Handled = True
End If
End Sub
NOTE: The regex has to validate (Maximum 2 decimal places, and 9 integers) with an optional percent symbol.
Please help, I feel so frustrated trying to solve the problem without success

I think that you almost had the right answer. When I run your regex against the samples you supplied, they all fail. But if I remove the extra space at the end of the regex I get the expected successes and failures.
So currently your regex looks like this:
Dim rex As Regex = New Regex("^[0-9]{1,9}([\.][0-9]{1,2})?[\%]?$ ")
and it should look like
Dim rex As Regex = New Regex("^[0-9]{1,9}([\.][0-9]{1,2})?[\%]?$")
EDIT:
Ok I understand the issue more. The problem with the regex is that it will only allow a period if it is followed by one or two numbers. That works fine if you are evaluating the textbox value after someone has finished typing. But in your code, you are evaluating for each keypress, so you don't have a chance to type a number after the "."
I can see two possible solutions
Change the regex to allow 1. as a valid entry
Change when you evaluate the regex, perhaps trying to figure out a way to only evaluate the regex when the person has paused typing.
If you went with option 1, then we need to tweak the regex to something like this
"^[0-9]{1,9}((\.)|(\.[0-9]{1,2}(%)?)|(%))?$"
I changed the regex so that it will accept three optional endings to the text string (\.) will allow the string to end in a period , (\.[0-9]{1,2}(%)?) will allow the string to end period followed by one or two numbers and an optional percent sign, and (%) will allow the string to end in a percent sign. I broke the ending into the three options because I didn't want to allow something like 12.% to be valid. Also for this to work you will also need to add the percent sign to your first If statement
If (Char.IsDigit(e.KeyChar) Or e.KeyChar.ToString() = "." Or e.KeyChar.ToString() = "%" Or e.KeyChar = CChar(ChrW(Keys.Back))) Then
so that the regex runs when someone types the percent sign.

Related

Parsing a time string using regex and converting to integer

I want to allow users to input a time in a masked text box and then validate that time and convert it if necessary for later saving.
I've tried a method of validating the time using only regex but honestly could not find very detailed answers. I decided to simply separate the string the user inputs into its base components and then convert the chunks of time into integers for easy comparison.
'''
Public Function CreateTimeString(TheTime As String, TheSuffix As String) As String 'wip
Dim Hour As String = "00"
Dim Minute As String = "00"
Dim inthour As Integer
Dim intminute As Integer
Dim pattern As String = "(?<hour>\d*?):(?<minute>\d*?)"
For Each m As Match In Regex.Matches(TheTime, pattern)
Hour = m.Groups("hour").Value
Minute = m.Groups("minute").Value
Next
inthour = Convert.ToInt32(Hour)
intminute = Convert.ToInt32(Minute)
TxtMeals.Text = Hour & ":" & Minute
End Function
'''
An error occurs when attempting to convert Minute string into an integer. Commenting this out and testing shows the Hour has been successfully converted. It appears that Minute cannot be found.
Example strings:
12:12
1:23
4:55
10:45
Also, if I change pattern by adding a space just before the last quotation mark neither are found and I would like to know why.
The m.Groups("minute").Value will not be found because you are using a non greedy match for \d*? for the minutes part there is no end boundary set like for example $ so it will match at least as possible which will be 0 times.
You could use:
(?<hour>\d+):(?<minute>\d+)
You might use a more precise match, for example for 12 hour time use:
(?<hour>1[0-2]|0?[1-9]):(?<minute>[0-5][0-9])
Or 24h time:
(?<hour>[01]?[0-9]|2[0-3]):(?<minute>[0-5][0-9])
You might opt to use anchors ^ and $ to assert the start and the end of the string.
Regex demo | vb.net demo

Regex to insert space with certain characters but avoid date and time

I made a regex which inserts a space where ever there is any of the characters
-:\*_/;, present for example JET*AIRWAYS\INDIA/858701/IDBI 05/05/05;05:05:05 a/c should beJET* AIRWAYS\ INDIA/ 858701/ IDBI 05/05/05; 05:05:05 a/c
The regex I used is (?!a\/c|w\/d|m\/s|s\/w|m\/o)(\D-|\D:|\D\*|\D_|\D\\|\D\/|\D\;)
I have added some words exceptions like a/c w/d etc. \D conditions given to avoid date/time values getting separated, but this created an issue, the numbers followed by the above mentioned characters never get split.
My requirement is
1. Insert a space after characters -:\*_/;,
2. but date and time should not get split which may have / :
3. need exception on words like a/c w/d
The following is the full code
Private Function formatColon(oldString As String) As String
Dim reg As New RegExp: reg.Global = True: reg.Pattern = "(?!a\/c|w\/d|m\/s|s\/w|m\/o)(\D-|\D:|\D\*|\D_|\D\\|\D\/|\D\;)" '"(\D:|\D/|\D-|^w/d)"
Dim newString As String: newString = reg.Replace(oldString, "$1 ")
formatColon = XtraspaceKill(newString)
End Function
I would use 3 replacements.
Replace all date and time special characters with a special macro that should never be found in your text, e.g. for 05/15/2018 4:06 PM, something based on your name:
05MANUMOHANSLASH15MANUMOHANSLASH2018 4MANUMOHANCOLON06 PM
You can encode exceptions too, like this:
aMANUMOHANSLASHc
Now run your original regex to replace all special characters.
Finally, unreplace the macros MANUMOHANSLASH and MANUMOHANCOLON.
Meanwhile, let me tell you why this is complicated in a single regex.
If trying to do this in a single regex, you have to ask, for each / or :, "Am I a part of a date or time?"
To answer that, you need to use lookahead and lookbehind assertions, the latter of which Microsoft has finally added support for.
But given a /, you don't know if you're between the first and second, or second and third parts of the date. Similar for time.
The number of cases you need to consider will render your regex unmaintainably complex.
So please just use a few separate replacements :-)

VB pattern with unknown number of digits

I am fairly new at VB and I need to write a function checking whether or not an ID is following the write pattern.
The pattern should be the letters ORG, accompanied by a - and then 1 to an infinite numbers of digits (ORG-1 or ORG-15793131354).
This is my code right now :
Sub CheckIdPattern
If MyFolder.Fields("USERTEXT221").value IsNot Nothing Then
Dim sId as String = MyFolder.Fields("USERTEXT221").value
Dim sMatch as Boolean = sId Like("ORG-#*")
If sMatch = False Then
Throw new exception("The ID entered is invalid, please use an ID starting with ORG- followed by numbers only")
End if
End if
End sub
As you can see I'm currently using "ORG-#*" which at least validate the beginning of the string, but permits to have any other characters at the end, causing bugs later in the program when we go and read these ID.
I also tried using System.Text.RegularExpressions (maybe not correctly though) but it failed because (I think) I can't import it in the program (I only have access to a small portion of the code and the rest is blocked by the software provider).
I know it seems like a pretty basic question so sorry for that and, thanks you very much for any help !
You can access the Regex.IsMatch method using a fully qualified name. Then, this code will do the trick for you:
Dim sId as String = "ORG-15793131354"
Dim sMatch as Boolean = System.Text.RegularExpressions.Regex.IsMatch(sId, "^ORG-\d+$")
If sMatch = False Then
Throw new exception("The ID entered is invalid, please use an ID starting with ORG- followed by numbers only")
End if
See IDEONE demo
The regex - "^ORG-\d+$" - matches ORG at the beginning of the string, then -, and then 1 or more digits up to the end of the string.
Perhaps you could check each character after your ORG- matching, using a for loop:
For i = 4 To sid.length
If Not(sid[i]>'0' And sid[i]<'9') Then
'throw your exception
End If
Next

Regex Returning extra empty Value

Set Regex = New RegExp
Regex.Pattern = """[^""]*""|[^,]*"
Regex.Global = True
//I have a for loop here to loop through records
text = Cells.Item(r, 7).Value
For Each Match In Regex.Execute(text)
count = count + 1
Next Match
This is my Regex Code, and here is the table where I am pulling the data from,
When I run the code in debug mode the PCBaa count comes up as two, c3 and c4 come up as 14 and C6-c36 come up as 36, Is my regex code wrong for extracting the codes between the commas ??
Ok, I have tried that myself and it seems that first off, it seems you don't reset the count value to 0 after each line. That could be intentional, but just so you know.
The second thing is that the regular expression seems to work nearly fine but always gives you the double amount because it matches a zero length string at the end of each match.
So for the last line (C6-C26) it machtes:
1) "C6" 2) "" 3) "C7" 4) "" ... and so on.
To be hounest, I'm a little bit surprised myself and don't exactly know why that's the case for now.
But the solution is pretty easy: Since you want there to be no zero length strings in the result (so they don't get counted) you simply have to exchange the * for a + and that will tell the regular expression to match only if there's at least one character.
So your regular expression string should look like:
Regex.Pattern = """[^""]+""|[^,]+"
Why you've got a count of 14 on the c3, c4 surprises me... I got a 4 which makes sence because of the double counting due to the zero length matches.

Regex Matching and Deleting/Replacing a string

So I am trying to parse through a file which has multiple "footers" (the file is an output that was designed for printing which my company wants to keep electronically stored...each footer is a new page and the new page is no longer needed as).
I am trying to look for and remove lines that look like:
1 of 2122 PRINTED 07/01/2013 04:46 Page : 1 of 11
2 of 2122 PRINTED 07/01/2013 04:46 Page: 2 of 11
3 of 2122 PRINTED 07/01/2013 04:46 Page: 3 of 11
and so on
I then want to replace the final line (which would read something like "2122 of 2122") with a "custom" footer.
I am using RegEx, but am very new to using it so how should my RegEx look in order to accomplish this? I plan on using the RegEx "count" function to find out when I've found the last line and then do a .replace on it.
I am using VB .NET, but can translate C# if required. How can I accomplish what I'm looking to do? Specifically I only care about matching/removing of a match so long as the # of matches > 1.
Here's one I created with RegExr:
/^(\d+\s+of\s+\d+)(?=\s+printed)/gim
It matches (number)(space)('of')(space)(number) at the beginning of a line, and only if it is followed by (space)('printed'), case insensitive. The /m flag turns ^ and $ into line-aware boundaries.
This is how I ended up doing it...
Private Function FixFooters(ByVal fileInput As String, Optional ByVal numberToLeaveAlone As Integer = 1) As String
Dim matchpattern As String = "^\d+\W+of\W+\d+\W+PRINTED.*$"
Dim myRegEx As New Regex(matchpattern, RegexOptions.IgnoreCase Or RegexOptions.Multiline)
Dim replacementstring As String = String.Empty
Dim matchCounter As Integer = myRegEx.Matches(fileInput).Count
If numberToLeaveAlone > matchCounter Then numberToLeaveAlone = matchCounter
Return myRegEx.Replace(fileInput, replacementstring, matchCounter - numberToLeaveAlone, 0)
End Function
I used myregextester.com to get the inital matchpattern. Since I wanted to leave the last footer alone (to manipulate it further later on) I created the numberToLeaveAlone variable to ensure we don't remove ALL of the variables. For the purposes of this program I made the default value 1, but that could be changed to zero (I only did it for readability in the calling code as I know I will ALWAYS want to leave one...but I do like to reuse code). It's fairly fast, I'm sure there are better ways out there, but this one made the most sense to me.