tags:

views:

468

answers:

8

Need a function that takes a character as a parameter and returns true if it is a letter.

+6  A: 

This was part of the code posted by rpetrich in response to a question by Joel Spolsky. I felt it needed a post specific to the problem it solves. It really is brilliant.

Private Function IsLetter(ByVal character As String) As Boolean
    IsLetter = UCase$(character) <> LCase$(character)
End Function

You may be thinking to yourself, "Will this always work?" The documentation on the UCase and LCase functions, confirms that it will:

UCase Function Only lowercase letters are converted to uppercase; all uppercase letters and nonletter characters remain unchanged.

LCase Function Only uppercase letters are converted to lowercase; all lowercase letters and nonletter characters remain unchanged.

raven
Doesn't this return true for letters above 0x7f? This isn't what was asked for, so it might not be what the questioner really wants.
Steve Jessop
Oh, hang on, you are the questioner. Oops. If it is what you want, you might change the question to stop people posting answers based on A-Z and a-z ranges.
Steve Jessop
Done. Good idea, since this method works with letters outside of a..z and A..Z.
raven
This is super-elegant, but only works for languages using latin alphabets that have upper and lower case variants.
Joel Spolsky
+1  A: 

What's wrong with the following, which doesn't rely on obscure language behaviour?

Private Function IsLetter(ByVal ch As String) As Boolean
    IsLetter = (ch >= "A" and ch <= "Z") or (ch >= "a" and ch <= "z")
End Function
paxdiablo
It only works for letters in English; it doesn't work for letters with accents.
Peter Hilton
Snap. We wrote almost the same code. Although - good point about the ANSI character sets.
seanyboy
It does work with letters with accents and circumflexes and tildes...
raven
A: 

It doesn't exactly document itself. And it may be slow. It's a clever hack, but that's all it is. I'd be tempted to be more obvious in my checking. Either use regex's or write a more obvious test.

public bool IsAlpha(String strToCheck)
{
    Regex objAlphaPattern=new Regex("[^a-zA-Z]");
    return !objAlphaPattern.IsMatch(strToCheck);
}

public bool IsCharAlpha(char chToCheck)
{
    return ((chToCheck=>'a') and (chToCheck<='z')) or ((chToCheck=>'A') and (chToCheck<='Z'))
}
seanyboy
Isn't that C#? A bit off-topic in a question tagged VB6?
MarkJ
A: 

Looking around a bit came up with the following...

Private Declare Function IsCharAlphaA Lib "user32" Alias "IsCharAlphaA" (ByVal cChar As Byte) As Long

I believe IsCharAlphaA tests ANSI character sets and IsCharAlpha tests ASCII. I may be wrong.

seanyboy
Like most Windows functions ...IsCharAlphaA is the ANSI versionIsCharAlphaW is the Unicode versionIsCharAlpha is a macro whoses behavior depends on whether UNICODE is defined
Joel Spolsky
That API declaration won't work on double-byte code pages - e.g. Chinese, Japanese. "ANSI" characters can be more than one byte on those code pages.
MarkJ
A: 
Private Function IsAlpha(ByVal vChar As String) As Boolean
  Const letters$ = "abcdefghijklmnopqrstuvwxyz"

  If InStr(1, letters, LCase$(vChar)) > 0 Then IsAlpha = True
End Function
Keith Maurino
+1  A: 
Private Function IsLetter(Char As String) As Boolean
    IsLetter = UCase(Char) Like "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]"
End Function
Graham Miller
+3  A: 

Seanyboy's IsCharAlphaA answer is close. The best method is to use the W version like so:

Private Declare Function IsCharAlphaW Lib "user32" (ByVal cChar As Integer) As Long
Public Property Get IsLetter(character As String) As Boolean
    IsLetter = IsCharAlphaW(AscW(character))
End Property

Of course, this all rarely matters as all of VB6's controls are ANSI only

rpetrich
+1, and give yourself more credit! You are correct and Seanyboy is wrong. Seanyboy's API declaration won't work on double-byte code pages - e.g. Chinese, Japanese. "ANSI" characters can be more than one byte on those code pages.
MarkJ
Yeah, your IsLetter implementation was neat, but not complete. This is much better.
raven
+1  A: 

I believe we can improve upon this a little more. rpetrich's code will work, but perhaps only by luck. The API call's parameter should be a TCHAR (WCHAR here actually) and not a Long. This also means no fiddling with converting to a Long or masking with &HFFFF. This by the way is Integer and adds an implicit conversion to Long here too. Perhaps he meant &HFFFF& in this case?

On top of that it might be best to explictly call the UnicoWS wrapper for this API call, for Win9X compatibility. The UnicoWS.dll may need to be deployed but at least we gain that option. Then again maybe from VB6 this is automagically redirected, I don't have Win9X installed to test it.

Option Explicit

Private Declare Function IsCharAlphaW Lib "unicows" (ByVal WChar As Integer) As Long

Private Function IsLetter(Character As String) As Boolean
    IsLetter = IsCharAlphaW(AscW(Character))
End Function

Private Sub Main()
    MsgBox IsLetter("^")
    MsgBox IsLetter("A")
    MsgBox IsLetter(ChrW$(&H34F))
    MsgBox IsLetter(ChrW$(&HFEF0))
    MsgBox IsLetter(ChrW$(&HFEFC))
End Sub
Bob Riemersma
You are right--As Integer is the proper declaration. I will edit my answer to reflect this. I still believe that user32 should be the default choice though; most times 95/98/ME support is not required and it's just more work to deploy unicows properly.
rpetrich