VBA - Identifying null string - excel

One of my cells appears to be blank but has a length of 2 characters. I copied the string to this website and it has identified it as a null string.
I have tried using IsNull and IsEmpty, as well as testing to see if it is equivalent to the vbNullString but it is still coming up as False.
How do I identify this string as being Null?

A string value that "appears to be blank but has a length of 2 characters" is said to be whitespace, not blank, not null, not empty.
Use the Trim function (or its Trim$ stringly-typed little brother) to strip leading/trailing whitespace characters, then test the result against vbNullString (or ""):
If Trim$(value) = vbNullString Then
The Trim function won't strip non-breaking spaces though. You can write a function that does:
Public Function TrimStripNBSP(ByVal value As String) As String
TrimStripNBSP = Trim$(Replace(value, Chr$(160), Chr$(32)))
End Function
This replaces non-breaking spaces with ASCII 32 (a "normal" space character), then trims it and returns the result.
Now you can use it to test against vbNullString (or ""):
If TrimStripNBSP(value) = vbNullString Then
The IsEmpty function can only be used with a Variant (only returns a meaningful result given a Variant anyway), to determine whether that variant contains a value.
The IsNull function has extremely limited use in Excel-hosted VBA, and shouldn't be needed since nothing is ever going to be Null in an Excel worksheet - especially not a string with a length of 2.

Chr(160) Issue
160 is the code number of a Non-Breaking Space.
Let us say the cell is A1.
In any cell write =CODE(A1) and in another (e.g. next to) write =CODE(MID(A1,2,1)).
The results are the code numbers (integers e.g. a and b) of the characters.
Now in VBA you can use:
If Cells(1, 1) = Chr(a) & Chr(b) Then
End If
or e.g.
If Left(Cells(1, 1), 1) = Chr(160) then
End If

Related

Best formula/method to extract a standard set of numbers from a string?

I have the following strings from which I need to extract 6 digit numbers. Since these strings are generated by another software, they occur interchangeably and I cannot control it. Is there any one method that would extract both 6-digit numbers from each of these strings?
Branch '100235 to 100236 Ckt 1' specified in table 'East Contingency' for record with primary key = 21733 was not found in branch or transformer data.
Loadflow branch ID '256574_701027_1' defined in supplemental branch table was not found in branch or transformer input.
Transmission element from bus number 135415 to bus number 157062 circuit ID = 1 defined for corridor 'IESO-NYISO' was not found in input data
I don't know VBA, but I can learn it if it means I can get the 6 digit numbers using a single method.
thanks
I have been using LEFT(), RIGHT() & MID() previously, but it means manually applying the appropriate formula for individual string.
If you have Microsoft 365, you can use this formula:
=LET(arr,TEXTSPLIT(SUBSTITUTE(SUBSTITUTE(A1,"'"," "),"_"," ")," "),
FILTER(arr,ISNUMBER(-arr)*(LEN(arr)=6)))
Thanks to #TomSharpe for this shorter version, using an array constant within TEXTSPLIT to add on possible delimiters.
=LET(arr,TEXTSPLIT(A1,{"_"," ",","," ","'"}),FILTER(arr,(LEN(arr)=6)*ISNUMBER(-arr)))
Data
Output
An alternative is:
=LET(ζ,MID(A1,SEQUENCE(,LEN(A1)-5),6),ξ,MID(ζ,SEQUENCE(6),1),FILTER(ζ,MMULT(SEQUENCE(,6,,0),1-ISERR(0+ξ))=6))
A couple more suggestions (if you need them):
(1) Replacing all non-digit characters with a space then splitting the resulting string:
=LET(numbers,TEXTSPLIT(TRIM(REDUCE("",MID(A1,SEQUENCE(1,LEN(A1)),1),LAMBDA(a,c,IF(is.digit(c),a&c,a&" "))))," "),FILTER(numbers,LEN(numbers)=6))
Here I've defined a function is.digit as
=LAMBDA(c, IF(c = "", FALSE, AND(CODE(c) > 47, CODE(c) < 58)))
(tl;dr I quite like doing it this way because it hides the implementation details of is.digit and creates a rudimentary form of encapsulation)
(2) A UDF - based on the example here and called as
=RegexTest(A1)
Option Explicit
Function RegexTest(s As String) As Double()
Dim regexOne As Object
Dim theNumbers As Object
Dim Number As Object
Dim result() As Double
Dim i As Integer
Set regexOne = New RegExp
' Not sure how you would extract numbers of length 6 only, so extract all numbers...
regexOne.Pattern = "\d+"
regexOne.Global = True
regexOne.IgnoreCase = True
Set theNumbers = regexOne.Execute(s)
i = 1
For Each Number In theNumbers
'...Check the length of each number here
If Len(Number) = 6 Then
ReDim Preserve result(1 To i)
result(i) = CDbl(Number)
i = i + 1
End If
Next
RegexTest = result
End Function
Note - if you wanted to preserve leading zeroes you would need to omit the Cdbl() and return the numbers as strings. Returns an error if no 6-digit numbers are found.

Remove Trailing Zeros from a Hexadecimal string

I have a column of Hexadecimal strings with many TRAILING zeros.
The problem i have is that the trailing Zeros from the string, needs to be removed
I have searched for a VBA formula such as Trim but my solution has not worked.
Is there a VBA formula I can use to remove all these Trailing zeros from each of the strings.
An example of the HEX string is 4153523132633403277E7F0000000000000000000000000000. I would like to have it in a format of 4153523132633403277E7F
The big issue is that the Hexadecimal strings can be of various lengths.
Formula:
You could try:
Formula in B1:
=LET(a,TEXTSPLIT(A1,,"0"),TEXTJOIN("0",0,TAKE(a,XMATCH("?*",a,2,-1))))
This would TEXTSPLIT() the input and the fact that we can then use XMATCH() to return the position of the last non-empty string with a wildcard match ?*. However, given the fact we can use arrays in our TEXTSPLIT() function, a little less verbose could be:
=TEXTBEFORE(A1,TAKE(TEXTSPLIT(A1,TEXTSPLIT(A1,"0",,1)),,-1),-1)
Or another option, though more verbose, is to use REDUCE() for what it's intended to do, which is to loop a given array:
=REDUCE(A1,SEQUENCE(LEN(A1)),LAMBDA(a,b,IF(RIGHT(a)="0",LEFT(a,LEN(a)-1),a)))
VBA:
If VBA is a must, one way of dealing with this is through the RTrim() function. Since your HEX-string should not contain spaces to begin with I think the following is a safe bet:
Sub Test()
Dim s As String: s = "4153523132633403277E7F0000000000000000000000000000"
Dim s_new As String
s_new = Replace(RTrim(Replace(s, "0", " ")), " ", "0")
Debug.Print s_new
End Sub
If you happen to have spaces anywhere else in your string, another option would be to look for trailing zero's using a regular expression:
Sub Test()
Dim s As String: s = "4153523132633403277E7F0000000000000000000000000000"
Dim s_new As String
With CreateObject("vbscript.regexp")
.Pattern = "0+$"
s_new = .Replace(s, "")
End With
Debug.Print s_new
End Sub
Both the above options should print: 4153523132633403277E7F
As far as I know, there is no function to do that for you. The way I would do it is presented in the pseudo-code below:
while last character is "0"
remove last character
end while
It is quit slow, but VBA itself is not race car either, so you will probably not notice especially if you do not need to that for many times at once.
A more beautiful solution would involve VBA being able to search for the beginning or the end of a string.
An improvement of the solution above is to parse the string backwards and count the "0" characters, and then remove them all at the same time.

VBA-Excel: How to get text enclosed in Quotation Mark from String

I have a String in VBA with this text: < History Version="1.10" Client="TestClient001" >
I want to get this TestClient001 or anything that's inside Client="xxxx"
I made this code but it's not working
Client = MID(text,FIND("Client=""",text)+1,FIND("""",text)-FIND("Client=""",text)-1)
Is there a way to specifically get the text inside Client="xxxx"?
There's no such function as Find in VBA - that's a worksheet function. The VBA equivalent is InStr, but I don't think you need to use it here.
The best tool for extracting one string from another in VBA is often Split. It takes one string and splits it into an array based on a delimiting string. The best part is that the delimiter doesn't have to be a single character - you can make it an entire string. In this case, we'd probably do well with two nested Split functions.
Client = Split(Split(text,"Client=""")(1),Chr(34))(0)
The inner Split breaks your text string where it finds "Client="". The (1) returns array element 1. Then the outer Split breaks that returned text where it finds a " character, and returns array element 0 as the final result.
For better maintainability, you may want to use constants for your delimiters as well.
Sub EnclosedTextTest()
Const csFlag1 As String = "Client="""
Const csFlag2 As String = """"
Const csSource As String = "< History Version=""1.10"" Client=""TestClient001"" >"
Dim strClient As String
strClient = Split(Split(csSource, csFlag1)(1), csFlag2)(0)
Debug.Print strClient
End Sub
However, if the Split method doesn't work for you, we can use a method similar to the one you were using, with InStr. There are a couple of options here as well.
InStr will return the position in a string that it finds a matching value. Like Split, it can be given an entire string as its delimiter; however, if you use more than one character you need to account for the fact that it will return where it finds the start of that string.
InStr(1,text,"Client=""")
will return 26, the start of the string "Client="" in the text. This is one of the places where it's helpful to have your delimiter stored in a constant.
intStart = InStr(1,text,csFlag1)+len(csFlag1)
This will return the location it finds the start of the delimiter, plus the length of the delimiter, which positions you at the beginning of the text.
If you store this position in a variable, it makes the next part easier as well. You can use that position to run a second InStr and find the next occurrence of the " character.
intEnd = InStr(intStart,text,csFlag2)
With those values, you can perform your mid. You code overall will look something like this:
Sub InstrTextTest()
Const csFlag1 As String = "Client="""
Const csFlag2 As String = """"
Const csSource As String = "< History Version=""1.10"" Client=""TestClient001"" >"
Dim strClient As String
Dim intPos(0 To 1) As Integer
intPos(0) = InStr(1, csSource, csFlag1) + Len(csFlag1)
intPos(1) = InStr(intPos(0), csSource, csFlag2)
strClient = Mid(csSource, intPos(0), intPos(1) - intPos(0))
Debug.Print strClient
End Sub
This will work, but I prefer the Split method for ease of reading and reuse.
You can make use of Split function to split at character = then with last element of the resulting array remove character quotes and > with help of replace function and you will get the required output.
In the end I got it thanks to the idea given by #alok and #Bigben
Dim cl() As String
Dim ClientCode As String
If (InStr(1, temp, "Client=", vbTextCompare) > 0) Then
cl = Split(temp, "=")
ClientCode = cl(UBound(cl))
ClientCode = Replace(ClientCode, """", "")
ClientCode = Replace(ClientCode, ">", "")
It's XML, so you could do this:
Dim sXML As String
sXML = "<History Version=""1.10"" Client=""TestClient001"">"
With CreateObject("MSXML.Domdocument")
.LoadXML Replace(sXML, ">", "/>") 'close the element
Debug.Print .FirstChild.Attributes.getnameditem("Client").Value
End With

VBA Function to bring back Left of a string, based on position of a specific character in the string

I'm trying to create a user defined function that returns everything to the left of the last hyphen in a string. All strings will be formatted like this:
CT2986-400427-15
J84705-202001-12
TF230068-003
DS2112-000038-11
This is my current code, but it keeps displaying a #VALUE! error in the cell. Any ideas?
Function PartNOSUFFIX(PartNo As String) As String
PartNOSUFFIX = Left(PartNo, InStr(8, PartNo, "-") - 1)
End Function
Thanks in advance!
You want to use Instrrev instead which is Instr but in reverse. This way it will always find the last hyphen and return it's position.
So try:
Function PartNOSUFFIX(PartNo As String) As String
PartNOSUFFIX = Left(PartNo, InStrRev(PartNo, "-") - 1)
End Function
You will get a #VALUE! error if the cell is blank or doesn't have a hyphen in it. If you want to account for that you can use something like this instead:
Function PartNOSUFFIX(PartNo As String) As String
If PartNo = "" Or InStr(PartNo, "-") = 0 Then
PartNOSUFFIX = ""
Else
PartNOSUFFIX = Left(PartNo, InStrRev(PartNo, "-") - 1)
End If
End Function
I just made it return blank if the cell it's looking at is blank or doesn't have a hyphen. You can change that to whatever you want it to say of course.
A bit complicated but fun try
Function PartNOSUFFIX(PartNo As String) As String
PartNOSUFFIX = Replace(PartNo, "-" & Split(PartNo, "-")(UBound(Split(PartNo, "-"))), "")
End Function
You can use StrReverse() and Split() to accomplish this quite easily:
Function PartNOSUFFIX(PartNo As String) As String
PartNOSUFFIX = StrReverse(Split(StrReverse(PartNo), "-", 1)(0))
End Function
Alternatively, you can use a regular expression:
Function PartNOSUFFIX(PartNo As String) As String
With CreateObject("vbscript.regexp")
.Pattern = "(.*)-.*"
PartNOSUFFIX = .Replace(PartNo, "$1")
End With
End Function
(.*) - Capture 0+ (greedy) characters other than newline in a capture group upto;
- - A literal hyphen.
.* - Again 0+ (greedy) characters other than newline.
The fact that regular expressions start matching left to right assures that our 1st greedy pattern will consume all characters upto the last hyphen since no other characters come after that leading to the next hyphen.
Note that both options would work even no hyphen is present.

How can I display in an array in vb

Dim textstring as String,warray() as String
textstring=Range("A3").Value
warray=split(textstring,"")
If isDate(warray(0))=True Then
Range("A4").Value=warray(1)
End If
This code displays only one word, no other words are displayed.
Typically, the vba Split function is used to split a string into a multiple element array on a delimiter. If a space was used as the delimiter, a sentence or paragraph would be split into individual words.
You are providing a zero-length string (e.g. "" ) as the delimiter. From the msdn docs:
... If delimiter is a zero-length string, a single-element array containing the entire expression string is returned.
So if you want to actually split something into multiple pieces, you cannot use a zero-length string as the delimiter.
If you used a space as the delimiter and provided no other parameters, then your example would be split into four elements of the array. I suspect you only want to split off the time and leave the show title together. The limit parameter is used for this. A limit of 2 will split your original string into a time and a show title.
Option Explicit
Sub Macro3()
Dim textstring As String, warray() As String
textstring = Range("A3").Value
warray = Split(expression:=textstring, delimiter:=" ", limit:=2)
If IsDate(warray(0)) Then
Range("A4").Value = warray(0)
Range("A5").Value = warray(1)
End If
End Sub

Resources