Need a function that takes a character as a parameter and returns true if it is a letter.
-
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 FunctionYou 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.
Steve Jessop : 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.raven : Done. Good idea, since this method works with letters outside of a..z and A..Z.Joel Spolsky : This is super-elegant, but only works for languages using latin alphabets that have upper and lower case variants. -
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 FunctionPeter Hilton : It only works for letters in English; it doesn't work for letters with accents.seanyboy : Snap. We wrote almost the same code. Although - good point about the ANSI character sets.raven : It does work with letters with accents and circumflexes and tildes... -
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')) }MarkJ : Isn't that C#? A bit off-topic in a question tagged VB6? -
Looking around a bit came up with the following...
Private Declare Function IsCharAlphaA Lib "user32" Alias "IsCharAlphaA" (ByVal cChar As Byte) As LongI believe IsCharAlphaA tests ANSI character sets and IsCharAlpha tests ASCII. I may be wrong.
Joel Spolsky : Like most Windows functions ... IsCharAlphaA is the ANSI version IsCharAlphaW is the Unicode version IsCharAlpha is a macro whoses behavior depends on whether UNICODE is definedMarkJ : 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. -
Private Function IsAlpha(ByVal vChar As String) As Boolean Const letters$ = "abcdefghijklmnopqrstuvwxyz" If InStr(1, letters, LCase$(vChar)) > 0 Then IsAlpha = True End Function -
Private Function IsLetter(Char As String) As Boolean IsLetter = UCase(Char) Like "[ABCDEFGHIJKLMNOPQRSTUVWXYZ]" End Function -
Seanyboy's
IsCharAlphaAanswer 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 PropertyOf course, this all rarely matters as all of VB6's controls are ANSI only
MarkJ : +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.raven : Yeah, your IsLetter implementation was neat, but not complete. This is much better. -
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 Subrpetrich : 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.
0 comments:
Post a Comment