Concept needed for building consistent urls (routes) - asp.net

My project has the need to build consistent urls similar to the ones here on stackoverflow. I know how I "can" do it by running the string through multiple filters, but I'm wondering if I can do it all with a single method.
Basically I want to remove all special characters, and replace them with dashes BUT if there are multiple dashes in a row, I need them to be a single dash. How can I implement this as clean as possible?
Example: If I were to use the following string.
My #1 Event
My regex would create the following string
my--1-event
notice how there are two dashes (one for the space and one for the "#" symbol). What I need is
my-1-event
Here's how I'm implementing it currently
''# <System.Runtime.CompilerServices.Extension()>
Public Function ToUrlFriendlyString(ByVal input As String) As String
Dim reg As New Regex("[^A-Za-z0-9]")
''# I could run a loop filter here to match "--" and replace it with "-"
''# but that seems like more overhead than necessary.
Return (reg.Replace(Trim(input), "-"))
End Function
And then all I do is call the extension method
Dim UrlFriendlyString = MyTile.ToUrlFriendlyString
Thanks in advance.

Add a + to the end of the regex.
This will tell it to match one or more characters that match the character class that precedes the +.
Also, you should create your Regex instance in a Shared field outside the method so that .Net won't need to parse the regex again every time you call the method.
[edited by rockinthesixstring]: here's the final result
Private UrlRegex As Regex = New Regex("[^a-z0-9]+", RegexOptions.IgnoreCase)
<System.Runtime.CompilerServices.Extension()>
Public Function ToUrlFriendlyString(ByVal input As String) As String
Return (UrlRegex.Replace(Trim(input), "-"))
End Function

Another way I do this without using a regex and also is a little simpler to understand is the following:
Excuse me on my vb as I am mainly C# guy.
''# <System.Runtime.CompilerServices.Extension()>
Public Function ToUrlFriendlyString(ByVal input As String) As String
If [String].IsNullOrEmpty(s) = True Then
Return [String].Empty
End If
Dim builder As New StringBuilder()
Dim slug = input.Trim().ToLowerInvariant()
For Each c As Char in slug
Select Case c
Case ' '
builder.Append("-")
Case '&'
builder.Append("and")
Case Else
If (c >= '0' And c <= '9') OrElse (c >= 'a' And c <= 'z') And c != '-')
builder.Append(c)
End If
End Select
Next
Return builder.ToString()
End Function

Related

string.split()(1) removes first character

I am running into an issue when i split a string on "_Pub" and get the back half of the string it removes the first character and I don't understand why or how to fix it unless i add the character back in
strFilePath = "/C:/Dev/Edge/_Publications/Ann Report/2013-2016/2016 Edge.pdf"
Dim relPath = strFilepath.Split("_Publications")(1)
lb.CommandArgument = relPath
returns Publications\Ann Report\2013-2016\2016 Edge.pdf
What you have as a delimiter is not a string array "string()" but a regular string. You need a string array to use a string as a delimiter. otherwise it takes the first char of your string.
https://msdn.microsoft.com/en-us/library/tabh47cf(v=vs.110).aspx
try this
Dim relPath = strFilepath.Split(new string() {"_Publications"}, StringSplitOptions.RemoveEmptyEntries)(1)
It appears that you want to get the part of the path starting at some directory. Splitting the path might not be such a good idea: imagine if there was a file "My_Publications_2017.pdf" in a directory "C:\Dev\Edge\_Publications". The split as you intended in the question would give the array of strings {"C:\Dev\Edge\", "\My", "_2017.pdf"}. As has been pointed out elsewhere, the String.Split you used doesn't do that anyway.
A more robust way would be to find where the starting directory's name is in the full path and get the substring of the path starting with it, e.g.:
Function GetRelativePath(fullPath As String, startingDirectory As String) As String
' Fix some errors in how the fullPath might be supplied:
Dim tidiedPath = Path.GetFullPath(fullPath.TrimStart("/".ToCharArray()))
Dim sep = Path.DirectorySeparatorChar
Dim pathRoot = sep & startingDirectory.Trim(New Char() {sep}) & sep
Dim i = tidiedPath.IndexOf(pathRoot)
If i < 0 Then
Throw New DirectoryNotFoundException($"Cannot find {pathRoot} in {fullPath}.")
End If
' There will be a DirectorySeparatorChar at the start - do not include it
Return tidiedPath.Substring(i + 1)
End Function
So,
Dim s = "/C:/Dev/Edge/_Publications/Ann Report/2013-2016/2016 Edge.pdf"
Console.WriteLine(GetRelativePath(s, "_Publications"))
Console.WriteLine(GetRelativePath(s, "\Ann Report"))
outputs:
_Publications\Ann Report\2013-2016\2016 Edge.pdf
Ann Report\2013-2016\2016 Edge.pdf
Guessing that you might have several malformed paths starting with a "/" and using "/" as the directory separator character instead of "\", I put some code in to mitigate those problems.
The Split() function is supposed to exclude the entire delimiter from the result. Could you re-check & confirm your input and output strings?

How to convert Linq.IEnumerable(Of String) to formatted Strings

I have a List(Of String) that stores validation errors. Should that list contain any items, I'd like to concatenate them into an HTML list to show each error. Currently this is easily done like so:
Dim l As List(Of String) = GetErrors()
If l.Count > 0 Then
Dim sb As New StringBuilder
sb.Append("<div><ul>")
For Each s As String In l
sb.Append(String.Format("<li>{0}</li>", s))
Next
sb.Append("</ul></div>")
ltl_status.Text = sb.ToString()
End If
However, because this is quite lengthy, I wondered whether Linq could provide a shortcut. I tried this (line breaks added for clarity):
If l.Count > 0 Then
ltl_status.Text = String.Format("<div class=""failure""><ul>{0}</ul></div>",
(
From s As String In l Select
String.Format("<li>{0}</li>", s)
)
)
End If
However, given that IEnumerable is a collection, the end result is just this output in the Literal:
System.Linq.Enumerable+WhereSelectListIterator`2[System.String,System.String]
The aim here is to build the list using the least possible lines of code. I see that String.Join accepts an IEnumerable parameter, but that simply joins the items together, whereas here I need to add additional strings to the beginning and end of each item. Is it possible?
Answer
Based on Jon Skeet's excellent suggestions, the extension method has saved me a ton of time and effort:
Public Module CollectionSignatureMethods
''' <summary>
''' Takes each String value in a String collection, reformats using a format, and then returns all as a single String.
''' </summary>
''' <param name="ie">An IEnumerable(Of String) collection of string values</param>
''' <param name="formatString">The string format (as per String.Format)</param>
''' <returns>All of the Strings from the collection, reformatted and combined to a single String</returns>
''' <remarks>Jon Skeet is the daddy(!)</remarks>
<Extension()> _
Public Function JoinFormat(ie As IEnumerable(Of String), formatString As String) As String
Return String.Join(String.Empty, ie.Select(Function(s As String) String.Format(formatString, s)))
End Function
End Module
It sounds like you need a combination of Join and Select:
String.Join("", _
l.Select(Function(s as String) String.Format("<li>{0}</li>", s))
Of course, you could always write your own JoinFormat extension method - that wouldn't be hard to do, and would be potentially useful all over the place. For example, you might then have:
ltl_status.Text = String.Format("<div class=""failure""><ul>{0}</ul></div>",
l.JoinFormat("<li>{0}</li>"));

Using arrays with asp.net and VB

Sorry, I'm sure this is something that has been covered many times, but I can't find quite what I am after.
I have a single row data table which contains various settings which are used within my web system. I have been toying with turning this into an XML document instead of the single row datatable, would that make more sense?
Anyway, so, given that this is one record, there is a field called "locations," this field contains data as follows:
locationName1,IpAddress|locationName2,IpAddress|etc
The ipAddress is just the first 5 digits of the IP and allows me to ensure that logins to certain elements (admin section managed by staff) can only be accepted when connected from a company computer (ie using our ISP) - this is a largely unnecessary feature, but stops kids I employ logging in at home and stuff!
So, as the user logs in, I can check if the IP is valid by a simple SQL query.
SELECT ips FROM settings WHERE loginLocations LIKE '%" & Left(ipAddress, 5) & " %'
What I need to be able to do now, is get the name of the users location from the dataField array.
I've come up with a few long winded looping procedures, but is there a simple way to analyse
locationName1,IpAddress1|locationName2,IpAddress2|etc
as a string and simply get the locationName where LoginIp = IpAddressX
... or am I going about this in a totally ridiculous way and should turn it into an xml file? (which will then create a whole load of other questions for you about parsing XML!!)
u can split the string in Vb.net and the send it to a query or anything
'Split the string on the "," character
Dim parts As String() = s.Split(New Char() {","c})
In SQL Server, these are the functions of interest in extracting a sub-string:
CHARINDEX
SUBSTRING
LENGTH
You can google the details about them (e.g. CHARINDEX at http://msdn.microsoft.com/en-us/library/ms186323.aspx). With these, you'll need to use computed columns in your query to extract that location name.
If it's not too late, you may want to devise a new way to layout these elements. For instance, instead of:
locationName1,IpAddress1|locationName2,IpAddress2|etc
How about
{IpAddress1,locationName1}{IpAddress2,locationName2}etc
That way you can use CHARINDEX to locate "{" & Left(ipAddress, 5); from that position, locate ","; then from that position locate the closing "}". From there it should be straightforward to use SUBSTRING to get at the locationName. Be forewarned that this will likely be a messy query (probably built from a few sub-queries, one for each position).
In the end, SpectralGhost's idea to just read in the column and do the extraction in VB is probably the way with the least hassle.
I've decided to do it this way, but would appreciate comments on efficiency.
I've split ipAddress and locationName into two database columns, within the one row, and getting the required data by comparing strings as arrays, as per the code below.
For this particular application I'm only dealing with one record, so it's pretty simple. However, further down the line, I need to product a system for monitoring product sales. from an invoices table.
Each invoice record is a row in the database with items in the invoice held similarly, [item1],[item2] etc. There is another column for quantities [qty1],[qty2] etc, prices [price1],[price2], etc. I'll need to be able to search the database for invoices in which an item number occurs (easy SQL WHERE itemList LIKE %[invNO]%) and then compare arrays to get the quantities and individual prices of each item on that invoice. Extracting these rows as arrays and locating the relevant position in these as per the code below, will work fine, but when the whole operation is looping through several hundred or thousand rows, will this become really slow?
ipList, locationList = list as comma separated string from database record field
Dim ipArray As Array = Split(ipList, ",")
Dim locationArray As Array = Split(locationList, ",")
For i = 0 To UBound(ipArray)
If Left(ipArray(i), 5) = Left(ipAddress, 5) Then
arrayPosition = i
itemFound = "True"
Exit For
End If
Next
location = locationArray(arrayPosition)
'loop through ips then get the position and make the location equal to that
If itemFound <> "True" Then
inValidIP = "True"
End If
I strongly advise that you break those our into database columns and each location is a separate row; had it been done that way in the first place, you wouldn't have the problem you have now.
But since you have it in a single row/column, why not bring that back and simply get the value from .NET?
Dim Source As String = "TestLocationA,127.0|TestLocationB,128.0|TestLocationC,129.0"
Dim Test As String = Source
Dim ToFind As String = "127.0"
Test = Test.Substring(0, Test.IndexOf(ToFind & "|") - 1)
Test = Test.Substring(Test.LastIndexOf("|") + 1)
MsgBox(Test)
OR
Public Class Form1
Private IpLocationList As New List(Of IpLocation)
Private IpToFind As String = ""
Private Class IpLocation
Public Name As String
Public IP As String
Public Sub New(ByVal FullLocation As String)
Me.Name = FullLocation.Substring(0, FullLocation.IndexOf(","))
Me.IP = FullLocation.Substring(FullLocation.IndexOf(",") + 1)
End Sub
End Class
Private Function FindIP(ByVal IpLocationItem As IpLocation) As Boolean
If IpLocationItem.IP = IpToFind Then
Return True
Else
Return False
End If
End Function
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim Source As String = "TestLocationA,127.0|TestLocationB,128.0|TestLocationC,129.0"
Dim LocationList As New List(Of String)
LocationList.AddRange(Split(Source, "|"))
For Each LocationItem As String In LocationList
IpLocationList.Add(New IpLocation(LocationItem))
Next
IpToFind = "127.0"
Dim result As IpLocation = IpLocationList.Find(AddressOf FindIP)
If result IsNot Nothing Then
MsgBox(result.Name)
End If
End Sub
End Class
enter code here
This has the benefit of loading the array one time and not needing to manually loop through arrays.

Detect culture of number in VB.Net i.e. period or comma for decimal point / thousands separator

In VB.Net, is there a way of auto-detecting the culture of a string representation of a number? I'll explain the situation:
Our asp.net web site receives xml data feeds for boat data. Most of the time, the number format for the prices use either a simple non-formatted integer e.g. "999000". That's easy for us to process.
Occaisionally, there are commas for thousands separators and periods for the decimal point. Also, that's fine as our data import understands this. Example "999,000.00".
We're starting to get some data from France where some of the prices have been entered with the periods and thousands separators the other way around as that's the way it's done in many European countries. E.g. "999.000,00". This is where our system would interpret that as nine hundred and ninety nine pounds instead of the nine hundred and ninety nine thousand pounds that was intended.
Unfortunately, the data feed contains prices in a mixture of the formats without any culture indicator on each one. Does anyone know of any in-built .net functions that will auto-detect the culture of a string number based on where the period and comma are?
I found a thread on vbforums suggesting how to get the decimal separator by culture.
I made a converter that converts between different files using oledb, excel object, mysql and more. Using "decimalSeparator" was the solution to get the number format working correctly when I converted to Excel and Access files.
Dim decimalSeparator As String = Globalization.CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator
cellValue = cellValue.Replace(".", decimalSeparator ).Replace(",", decimalSeparator )
There is no built-in way to determine the CultureInfo from a numeric string, as far as I know. And I seriously doubt it'll ever be, because there is no 100% safe way to do it.
Until you find a better solution (eg: some change on the sender-side), I guess the best you can do is to decrease the chances of error in two steps:
1) Input data cleanup and standardization:
Dim input as String = " 99 9.000,00 "
' This way you can remove unwanted characters (anything that is not a digit, and the following symbols: ".", "-", ",")
Dim fixedInput as String = Regex.Replace(input, "[^\d-,\.]", "")
' fixedInput now is "999.000,00"
2) Guess yourself the format:
Dim indexOfDot as Integer = fixedInput.IndexOf(".")
Dim indexOfComma as Integer = fixedInput.IndexOf(",")
Dim cultureTestOrder as List(Of CultureInfo) = new List(Of CultureInfo)
Dim parsingResult as Double?
Try
If indexOfDot > 0 And indexOfComma > 0 Then
' There are both the dot and the comma..let's check their order
If indexOfDot > indexOfComma Then
' The dot comes after the comma. It should be en-US like Culture
parsingResult = Double.Parse(fixedInput, NumberStyles.Number, CultureInfo.GetCultureInfo("en-US"))
Else
' The dot comes after the comma. It should be it-IT like Culture
parsingResult = Double.Parse(fixedInput, NumberStyles.Number, CultureInfo.GetCultureInfo("it-IT"))
End If
Else If indexOfDot = fixedInput.Length-3 Then
' There is only the dot! And it is followed by exactly two digits..it should be en-US like Culture
parsingResult = Double.Parse(fixedInput, NumberStyles.Number, CultureInfo.GetCultureInfo("en-US"))
Else If indexOfComma = fixedInput.Length-3 Then
' There is only the comma! And it is followed by exactly two digits..it should be en-US like Culture
parsingResult = Double.Parse(fixedInput, NumberStyles.Number, CultureInfo.GetCultureInfo("it-IT"))
End If
Catch
End Try
If Not parsingResult.HasValue Then
Try
' There is no dot or comma, or the parsing failed for some reason. Let's try a less specific parsing.
parsingResult = Double.Parse(fixedInput, NumberStyles.Any, NumberFormatInfo.InvariantInfo)
Catch
End Try
End If
If Not parsingResult.HasValue Then
' Conversion not possible, throw exception or do something else
Else
' Use parsingResult.Value
End If
You are not 100% safe this way, but it should be still better than your current code (and at least works as expected on the example data your provided).
Without knowing the specifics of how many differne ways the string can be formatted... This works for the three examples you gave
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
Dim v As String = "999.123,45"
Debug.WriteLine(foo(v))
v = "999,123.45"
Debug.WriteLine(foo(v))
v = "999123"
Debug.WriteLine(foo(v))
End Sub
Private Function foo(value As String) As Double
Dim style As NumberStyles = NumberStyles.AllowThousands Or NumberStyles.AllowDecimalPoint
Dim culture As CultureInfo = CultureInfo.InvariantCulture
Dim rv As Double
If Double.TryParse(value, style, culture, rv) Then
Debug.WriteLine(",.Converted '{0}' to {1}.", value, rv)
Else
Dim styleES As NumberStyles = NumberStyles.AllowThousands Or NumberStyles.AllowDecimalPoint
Dim cultureES As CultureInfo = CultureInfo.CreateSpecificCulture("es-ES")
If Double.TryParse(value, styleES, cultureES, rv) Then
Debug.WriteLine(".,Converted '{0}' to {1}.", value, rv)
Else
Throw New ArgumentException
End If
End If
Return rv
End Function
'This variable is True in case the O.S. Regional settings use a dot as decimal separator, false in the other cases.
Dim bAmerican as boolean=Cdec("0,2") > 1
Original post:
https://www.linkedin.com/groups/8141257/8141257-6347113651027079171

Getting the Request Variables from an ASP.NET page

I wrote the following function that works about 95% of the time, but I need it to work 100% (obviously):
Public Shared Function getPassedVars() As String
Const keyCount As Integer = 54 ' 54 seems to be the number of parameter keys passed by default (for this web_app).
' there are more if there is a form involved (ie. from search page)
Dim oParams As String = ""
Try
With HttpContext.Current
If .Request.Params.AllKeys.Count > keyCount Then
For i As Integer = 0 To (.Request.Params.AllKeys.Count - (keyCount + 1))
oParams &= String.Format("{0}={1}{2}", .Request.Params.Keys.Item(i), .Request.Params(i), IIf(i < .Request.Params.AllKeys.Count - (keyCount + 1), ";", ""))
Next
End If
End With
Return oParams
Catch ex As Exception
Return Nothing
End Try
End Function
It scrubs the Request.Params object for passed variables, which are in the beginning of the array (the remaining ones are ASP parameters). I am pretty sure I've seen a different way to get these parameters, but I haven't been able to figure it out. Any suggestions?
EDIT
So it looks like I can use the Request.URL.Query to achieve this, I will investigate this and post back.
Here is what I came up with:
Public Shared Function getPassedVars() As String
Dim oParams As String = ""
Dim qString As String = ""
Dim oSplit As New List(Of String)
Try
With HttpContext.Current
qString = .Request.Url.Query
If qString.Length > 0 Then 'do we have any passed variables?
If qString.StartsWith("?") Then qString = qString.Remove(0, 1) 'remove leading ? from querystring if it is there
oSplit.AddRange(qString.Split("&"))
For i As Integer = 0 To oSplit.Count - 1
oParams &= String.Format("{0}{1}", oSplit.Item(i), IIf(i < oSplit.Count - 1, ";", ""))
Next
Return oParams
Else
Return Nothing
End If
End With
Catch ex As Exception
Return Nothing
End Try
End Function
So far so good.
Request.QueryString is a NameValueCollection, so the easiest way to get the "parameters" is to do the following:
foreach (String s in Request.QueryString) {
Response.Write(s + " = " + Request.QueryString[s]);
}
Where is your function located? If it's executing in the page's code behind then you definitely do not need to use the HttpContext variable.
It looks like you are trying to get values from the query string.
For example, for this URL:-
http://www.tempuri.org/mypage.aspx?param1=x&param2=y
I assume you want retreive the values of the query string parameters param1 and param2?
If so, just use:-
Dim param1 as String = Request.QueryString("param1")
Otherwise, if these parameters are contained in a form (an HTTP POST request) then use the method which Mitchel Sellers suggests.
If you know the name you can use the following to get it by key value
Dim myParamValue as String = Request.Form("MyKeyName")
Otherwise, you can loop through the form collection, by key etc, to get the values. The key is, do you really need to be parsing all 54 items? Or are you simply looking for a few specific values?
httpcontext.Current.Request.QueryString("KeyName")
Request.Params will contain the query parameters you're after.
There's no need to parse the info from Request.URL since it's already done for you.

Resources