Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Best way for non-alphanumeric

Status
Not open for further replies.

foxmuldr3

Programmer
Jul 19, 2012
166
US
What's the fastest way to take an input string of unknown length, and determine if it ONLY consists entirely of alphanumeric digits?

Code:
lcX  = ALLTRIM(table.field)  && non-binary memo field varying from 0 to ~6,000 bytes
llAN = .t.                   && flag for "is alphanumeric?"
FOR lnI = 1 to LEN(lcX)
    lcC = SUBSTR(lcX,lnI,1)
    IF NOT ISALPHA(lcC) OR NOT ISDIGIT(lcC)
        * It is something other than A-Z_a-z_0-9
        llAN = .f.
        EXIT
    ENDIF
NEXT
* llAN holds the AlphaNumeric status

Better methods?

Best regards,
Rick C. Hodgin
 
Maybe something like this:

Code:
lcX  = UPPER(ALLTRIM(table.field))
lcAlpha = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
llAN = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)

Note: Not tested.

I don't know if this will be faster. You'd have to run some tests with your actual typical data to find out.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips, training, consultancy
 
Using a simple sample font name, and iterating 25 million times:

Code:
CLEAR
lnCount = 25000000
? "FOR LOOP:"
? TIME()
table_field = "Times New Roman"
FOR lnI = 1 TO lnCount
	lcX  = ALLTRIM(table_field)  && non-binary memo field varying from 0 to ~6,000 bytes
	llAN = .t.                   && flag for "is alphanumeric?"
	FOR lnJ = 1 to LEN(lcX)
	    lcC = SUBSTR(lcX,lnI,1)
	    IF NOT ISALPHA(lcC) OR NOT ISDIGIT(lcC)
	        * It is something other than A-Z_a-z_0-9
	        llAN = .f.
	        EXIT
	    ENDIF
	NEXT
	* llAN holds the AlphaNumeric status
NEXT
? TIME()
? ""

? "CHRTRAN:"
? TIME()
FOR lnI = 1 TO lnCount
	lcX     = UPPER(ALLTRIM(table_field))
	lcAlpha	= "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
	llAN    = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
NEXT
? TIME()

Results show 9 second difference:

Code:
FOR LOOP:
12:12:30
12:13:00

CHRTRAN:
12:13:00
12:13:39
 
Fixing this obvious optimization I overlooked in my first post:

Code:
? "CHRTRAN:"
? TIME()
lcAlpha	= "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
FOR lnI = 1 TO lnCount
    lcX     = UPPER(ALLTRIM(table_field))
    llAN    = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
NEXT
? TIME()

Changed the results...

Code:
CHRTRAN:
12:19:53
12:20:25

...to 32 seconds, down from 39.
 
Interesting. How about trying this:

Code:
lcAlpha	= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
FOR lnI = 1 TO lnCount
    lcX     = ALLTRIM(table_field)
    llAN    = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
NEXT

Probably won't improve matters, but worth a try perhaps.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips, training, consultancy
 
Code:
CLEAR
lnCount = 25000000
? "FOR LOOP:"
? TIME()
table_field = "Times New Roman"
FOR lnI = 1 TO lnCount
	lcX  = ALLTRIM(table_field)  && non-binary memo field varying from 0 to ~6,000 bytes
	llAN = .t.                   && flag for "is alphanumeric?"
	FOR lnJ = 1 to LEN(lcX)
	    lcC = SUBSTR(lcX,lnI,1)
	    IF NOT ISALPHA(lcC) OR NOT ISDIGIT(lcC)
	        * It is something other than A-Z_a-z_0-9
	        llAN = .f.
	        EXIT
	    ENDIF
	NEXT
	* llAN holds the AlphaNumeric status
NEXT
? TIME()
? ""

? "CHRTRAN AZ_09:"
? TIME()
lcAlpha	= "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
FOR lnI = 1 TO lnCount
	lcX     = UPPER(ALLTRIM(table_field))
	llAN    = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
NEXT
? TIME()
? ""

? "CHRTRAN AZ_az_09:"
? TIME()
lcAlpha	= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
FOR lnI = 1 TO lnCount
    lcX     = ALLTRIM(table_field)
    llAN    = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
NEXT 
? TIME()

Results:

Code:
FOR LOOP:
12:42:43
12:43:14   && 31 seconds

CHRTRAN AZ_09:
12:43:14
12:43:46   && 32 seconds

CHRTRAN AZ_az_09:
12:43:46
12:44:26   && 40 seconds
 
Your solution has a flaw: You shoudl test for NOT (ISALPHA(lcC) OR ISDIGIT(lcC)), which could be changed by boolean algebra to NOT ISALPHA(lcC) AND NOT ISDIGIT(lcC), but not as you have it NOT ISALPHA(lcC) OR NOT ISDIGIT(lcC).

Mikes solution is faster, but StrFilter of Foxtools is even faster.

M;y Teststring is replicate("a",6000)
Your loop (with corrected IF condition) takes 6.4 seconds for running 1000 times, the CHRTRAN test takes 4.3 seconds and StrFilter takes 0.06 seconds(!):

Set Library To FoxTools Additive
lcAlphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
lcX = ALLTRIM(REPLICATE("a",6000)) && non-binary memo field varying from 0 to ~6,000 bytes
t1=SECONDS()
FOR lnCount= 1 TO 1000
llAN = LEN(lcX)<LEN(StrFilter(lcX,lcAlphaNumeric))
ENDFOR
? SECONDS()-t1

It should better be tested with different inputs, maybe that only works out for this specific case, as a also is the first char tested. To test replicate("9",6000), StrFilter takes 0.25 seconds. So it looks like you can improve the strFilter time by putting the most frequent letters first, eg first all vocals. Also you could take Mikes idea to do an UPPER, which lowers the need to test against all lower alphabetical chars, of course, but also does process the string once, fully, as a StrFilter() does. So it may pay or not pay to do that.

Unfortunately we don't have pointers in Foxpro, for this case it would be better to let a pointer go through all string chars, of course.

Bye, Olaf.
 
Sorry,

Code:
lcAlpha = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
lcX  = Alltrim(Replicate("9",6000))  && non-binary memo field varying from 0 to ~6,000 bytes
t1=Seconds()
For lnCount= 1 To 1000
   llAN = Len(lcX)<Len(StrFilter(lcX,lcAlpha))
Endfor
? Seconds()-t1
? Len(StrFilter(lcX,lcAlpha))
Set Library To FoxTools

Your loop can be fast, if one of the first bytes already is nonprintable. And if you have some binary value inside the memo, that is very probable, it shows up in the first few chars. So overall, you could make a quick test with LEFT(memo,16) eg, and then only test records completely, which passed this first test.

Bye, Olaf.
 
In regard to english language, Wikipedia ( suggests this sort order for usage in StrFilter:

lcAlpha = " etaoinshrdlcumwfgypbvkjxqz0123456789ETAOINSHRDLCUMWFGYPBVKJXQZ.,-!?"

I also included space (as perhaps even more often than e) and punctuation characters. You might add other chars as á,à,â,etc.

Bye, Olaf.
 
What am I doing wrong?

Code:
lnCount		= 25000000
table_field	= "Times New Roman"

? "FOXTOOLS:"
t1=SECONDS()
SET LIBRARY TO FoxTools ADDITIVE
lcAlphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
lcX            = ALLTRIM(table_field)
FOR lnCount= 1 TO lnCount
	llAN = LEN(lcX) < LEN(StrFilter(lcX,lcAlphaNumeric))
ENDFOR
? SECONDS()-t1

I get the results:

Code:
FOXTOOLS:
        144.842

?!?!!! Win7 64-bit Core i5 HP notebook.
 
In your own test you will wrongly receive llAN = .F., your loop only is faster, because your check condition is wrong and just the first "T" already makes you exit the loop:

NOT ISALPHA("T") OR NOT ISDIGIT("T")
<=> NOT .T. OR NOT .F.
<=> .F. OR .T.
<=> .T.

See? That results in .T., so you set llAN = .f. and exit the loop always at the first char of "Times New Roman". With the corrected condition your loop takes 40 seconds, too, and it get's worse with longer text strings to test.

StrFilter() should be best, even in real test texts.

Bye, Olaf.
 
You're not doing anything wrong, it seems not only the char position in lcAlphaNumeric matters with StrFilter, but also, if the string to be tested consists of only one repeated char or different chars.

Still your loop isn't the fastest, if you mend your condition. You never determine .T., even with your test string.

Bye, Olaf.
 
Code:
CLEAR
lnCount		= 2500000
table_field	= "Times New Roman"

? "FOR LOOP:"
lcX  = ALLTRIM(table_field)  && non-binary memo field varying from 0 to ~6,000 bytes
t1=SECONDS()
FOR lnI = 1 TO lnCount
    llAN = .t.                   && flag for "is alphanumeric?"
    FOR lnJ = 1 to LEN(lcX)
        lcC = SUBSTR(lcX,lnI,1)
        IF NOT ISALPHA(lcC) AND NOT ISDIGIT(lcC)
            * It is something other than A-Z_a-z_0-9
            llAN = .f.
            EXIT
        ENDIF
    NEXT
    * llAN holds the AlphaNumeric status
NEXT
? STR(SECONDS()-t1, 8, 3)
? ""

? "CHRTRAN AZ_09:"
lcAlpha	= "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
lcX     = UPPER(ALLTRIM(table_field))
t1=SECONDS()
FOR lnI = 1 TO lnCount
	llAN = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
NEXT
? STR(SECONDS()-t1, 8, 3)
? ""

? "CHRTRAN AZ_az_09:"
lcAlpha	= "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
lcX     = ALLTRIM(table_field)
t1=SECONDS()
FOR lnI = 1 TO lnCount
    llAN = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
NEXT 
? STR(SECONDS()-t1, 8, 3)
? ""

? "FOXTOOLS:"
SET LIBRARY TO "C:\Program Files (x86)\Microsoft Visual FoxPro 9\FoxTools.fll" ADDITIVE
lcAlphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
lcX            = ALLTRIM(table_field)
t1=SECONDS()
FOR lnCount= 1 TO lnCount
    llAN = LEN(lcX) < LEN(StrFilter(lcX,lcAlphaNumeric))
ENDFOR
? STR(SECONDS()-t1, 8, 3)
? ""

? "C++ DLL:"
DECLARE INTEGER utils_is_AZaz09 IN exe\utils.dll STRING@ tcString, INTEGER tnStringLength
lcX = ALLTRIM(table_field)
t1=SECONDS()
FOR lnCount= 1 TO lnCount
	llAN = utils_is_AZaz09(@lcX, LEN(lcX))
ENDFOR
? STR(SECONDS()-t1, 8, 3)

Results:

Code:
FOR LOOP:
   2.754

CHRTRAN AZ_09:
   2.438

CHRTRAN AZ_az_09:
   3.320

FOXTOOLS:
  14.486

C++ DLL:
   3.537

Olaf, still don't know what I'm doing wrong with FOXTOOLS.
 
You're not doing anything wrong. StrFilter() only seems to work good in case you don't test "Times New Roman" but Replicate("a",6000). Sounds odd, but it seems to also depend on the number of distinct chars in the string to be filtered.

I wonder about the C++ DLL, I think it could be done faster, if used directly and not via DECLARE. Passing a variable, even by reference via STRING@, always has the overhead to copy a string, VFP has to convert it's string variable to a null terminated C string.

I once did the a C++ DLL implementing the Boyer-Moore algorithm for string matching, but it was slow, because of the memory copy of the test string being done.

Test it by implementing a second function "utils_is_AZaz09_byval" and pass on the string by value, it will most probably take a similar time.

Bye, Olaf.
 
Code:
CLEAR
lnCount    = 2500000
SET PRINTER TO \temp\t.t
SET PRINTER ON

DECLARE table_field[10]
table_field[1]    = "Times New Roman"
table_field[2]    = "Rick C. Hodgin"
table_field[3]    = "Foxtools"
table_field[4]    = "youtube anuna riu riu"
table_field[5]    = "JITTER bug"
table_field[6]    = "There once was a man who said ... 'Hi!'"
table_field[7]    = "Peanut butter jelly time"
table_field[8]    = "LPARAMETERS tcFoo, tnFoosFriend"
table_field[9]    = "RETURN .f."
table_field[10]    = "RUN /N cmd.exe start /AFFINITY 0x1 \fpw26\foxprow.exe"

DECLARE results[5, 10]
FOR lnRow = 1 TO 5
    FOR lnCol = 1 TO 10
        results[lnRow, lnCol] = 0.0
    NEXT
NEXT

DECLARE names[5]
names[1] = "FOR LOOP"
names[2] = "CHRTRAN AZ_09"
names[3] = "CHRTRAN AZ_az_09"
names[4] = "FOXTOOLS"
names[5] = "C++ DLL"

* Mixer passes:
* 1 - normal
* 2 - upper
* 3 - lower
* 4 - no spaces
FOR lnMixer = 1 TO 4
    FOR lnTestIteration = 1 TO ALEN(table_field)
        ? table_field[lnTestIteration]
*        ? names[1] + " " + ALLTRIM(STR(lnTestIteration,2,0))
        lcX  = ALLTRIM(table_field[lnTestIteration])  && non-binary memo field varying from 0 to ~6,000 bytes
        t1=SECONDS()
        FOR lnI = 1 TO lnCount
            llAN = .t.                   && flag for "is alphanumeric?"
            FOR lnJ = 1 to LEN(lcX)
                lcC = SUBSTR(lcX,lnI,1)
                IF NOT ISALPHA(lcC) AND NOT ISDIGIT(lcC)
                    * It is something other than A-Z_a-z_0-9
                    llAN = .f.
                    EXIT
                ENDIF
            NEXT
            * llAN holds the AlphaNumeric status
        NEXT
        results[1, lnTestIteration] = SECONDS()-t1

*        ? names[2] + " " + ALLTRIM(STR(lnTestIteration,2,0))
        lcAlpha    = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
        lcX     = UPPER(ALLTRIM(table_field[lnTestIteration]))
        t1=SECONDS()
        FOR lnI = 1 TO lnCount
            llAN = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
        NEXT
        results[2, lnTestIteration] = SECONDS()-t1

*        ? names[3] + " " + ALLTRIM(STR(lnTestIteration,2,0))
        lcAlpha    = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        lcX     = ALLTRIM(table_field[lnTestIteration])
        t1=SECONDS()
        FOR lnI = 1 TO lnCount
            llAN = (LEN(CHRTRAN(lcX, lcAlpha, "")) = 0) AND NOT EMPTY(lcX)
        NEXT 
        results[3, lnTestIteration] = SECONDS()-t1

**        ? names[4] + " " + ALLTRIM(STR(lnTestIteration,2,0))
*        SET LIBRARY TO "C:\Program Files (x86)\Microsoft Visual FoxPro 9\FoxTools.fll" ADDITIVE
*        lcAlphaNumeric = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
*        lcX            = ALLTRIM(table_field[lnTestIteration])
*        t1=SECONDS()
*        FOR lnCount= 1 TO lnCount
*            llAN = LEN(lcX) < LEN(StrFilter(lcX,lcAlphaNumeric))
*        ENDFOR
*        results[4, lnTestIteration] = SECONDS()-t1

*        ? names[5] + " " + ALLTRIM(STR(lnTestIteration,2,0))
        DECLARE INTEGER utils_is_AZaz09 IN exe\utils.dll STRING@ tcString
        lcX = ALLTRIM(table_field[lnTestIteration]) + CHR(0)
        t1=SECONDS()
        FOR lnCount= 1 TO lnCount
            llAN = utils_is_AZaz09(lcX)
        ENDFOR
        results[5, lnTestIteration] = SECONDS()-t1
    NEXT
    
    ? ""
    ? "Mixup pass " + STR(lnMixer,1,0)

    FOR lnRow = 1 TO ALEN(results,1)
        IF lnRow != 4 && skip foxtools
            ? "  " + names[lnRow] + ":"
            
            lnMin = 10000.0
            lnMax = 0.0
            lnTot = 0.0
            FOR lnCol = 1 TO ALEN(results,2)
                lnMin = MIN(lnMin, results[lnRow, lnCol])
                lnMax = MAX(lnMax, results[lnRow, lnCol])
                lnTot = lnTot + results[lnRow, lnCol]
            NEXT
            ? "    Min: " + ALLTRIM(STR(lnMin, 8, 3))
            ? "    Max: " + ALLTRIM(STR(lnMax, 8, 3))
            ? "    Avg: " + ALLTRIM(STR(lnTot / ALEN(results, 2), 8, 3))
        ENDIF
    NEXT
    
    * Mix everything
    FOR lnRow = 1 TO ALEN(table_field)
        * Each pass does something different
        DO CASE
            CASE lnMixer = 1
                * Force all upper-case
                table_field[lnRow] = UPPER(table_field[lnRow])
            
            CASE lnMixer = 2
                * Force all lower-case
                table_field[lnRow] = LOWER(table_field[lnRow])
            
            CASE lnMixer = 3
               * remove spaces
                table_field[lnRow] = STRTRAN(table_field[lnRow], SPACE(1), SPACE(0))
        ENDCASE
    NEXT
NEXT
SET PRINTER TO
SET CONSOLE ON

Results:

Code:
Strings:
Times New Roman
Rick C. Hodgin
Foxtools
youtube anuna riu riu
JITTER bug
There once was a man who said ... 'Hi!'
Peanut butter jelly time
LPARAMETERS tcFoo, tnFoosFriend
RETURN .f.
RUN /N cmd.exe start /AFFINITY 0x1 \fpw26\foxprow.exe

AS THEY ARE ABOVE:
  FOR LOOP:
    Min: 2.710
    Max: 2.758
    Avg: 2.723
  CHRTRAN AZ_09:
    Min: 1.900
    Max: 6.432
    Avg: 3.341
  CHRTRAN AZ_az_09:
    Min: 2.163
    Max: 9.455
    Avg: 4.565
  C++ DLL:
    Min: 1.871
    Max: 2.062
    Avg: 1.929
    
UPPER-CASE:
  FOR LOOP:
    Min: 2.691
    Max: 2.768
    Avg: 2.724
  CHRTRAN AZ_09:
    Min: 1.925
    Max: 6.480
    Avg: 3.349
  CHRTRAN AZ_az_09:
    Min: 1.912
    Max: 7.663
    Avg: 3.699
  C++ DLL:
    Min: 1.874
    Max: 2.009
    Avg: 1.924

LOWER-CASE:
  FOR LOOP:
    Min: 2.702
    Max: 2.733
    Avg: 2.718
  CHRTRAN AZ_09:
    Min: 1.914
    Max: 6.491
    Avg: 3.344
  CHRTRAN AZ_az_09:
    Min: 2.532
    Max: 10.334
    Avg: 4.919
  C++ DLL:
    Min: 1.875
    Max: 1.966
    Avg: 1.925

NO SPACES, LOWER-CASE:
  FOR LOOP:
    Min: 2.704
    Max: 2.762
    Avg: 2.724
  CHRTRAN AZ_09:
    Min: 1.875
    Max: 6.123
    Avg: 3.290
  CHRTRAN AZ_az_09:
    Min: 2.537
    Max: 9.578
    Avg: 4.700
  C++ DLL:
    Min: 1.773
    Max: 2.010
    Avg: 1.874

C++ source is here. Can be optimized more:

Code:
int utils_is_AZaz09(char* tcString)
{
    char c;

    // Iterate until we're done or find a non-matching character
    while (*tcString != 0)
    {
        c = *tcString;
        if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9'))
            return(0);        // Failure
    }
    // We're good.
    return(1);
}
 
The C++ algorithm does not have an increment in the code above. It was there in my actual source. I was editing code in the tek-tips editor window. :)

I repeated the code above with REPLICATE(x,100) put around each of the string definitions, and changed the iteration counts because the test was taking too long.

The results indicated undesirable side-effects in the CHRTRAN solution for long strings, in that it has to complete the character transformation BEFORE its results are obtained. This means if what would be the signaler byte for failure is early on in the string, CHRTRAN does not have that early out, whereas both FOR and C++ do.

Replicate 100 testing had range, average:

Code:
FOR LOOP 0.330 to 19.936, average 3.965 to 4.638.
CHRTRAN  1.797 to 33.433, average 8.428 to 9.513.
C++ DLL  0.020 to  0.123, average 0.320 to 0.058.

My final C++ algorithm ended up being:

Code:
int utils_is_AZaz09(char* tcString)
{
    char c;

    // Iterate until we're done or find a non-matching character
    while (*tcString != 0)
    {
        // Grab the character
        c = *tcString;

        // See if it's in a forbidden range
        if (!(c >= 'A' && c <= 'Z') && !(c >= 'a' && c <= 'z') && !(c >= '0' && c <= '9'))
            return(0);        // Failure

        // Move to next byte
        ++tcString;
    }
    // We're good.
    return(1);
}

Not having to push that second parameter on the stack by having a NULL-terminated string made a huge difference. I'm also surprised the difference in speed between lcX and @lcX.

Code:
* Using "Rick C. Hodgin" iterated 2,500,000 times:
 lcX = 1.812 
@lcX = 3.073

That was surprising to me.

Best regards,
Rick C. Hodgin
 
I was able to write an assembly version that is marginally faster on lcX, but marginally slower on @lcX. I don't know why.

Code:
__declspec(naked) u32 utils_is_AZaz09(s8* tcString)
{
    _asm {
        mov        esi,[esp+4]
top_loop:
        movzx      eax,byte ptr [esi]
        cmp        eax,0
        jz         done

        cmp        eax,'0'
        jb         failure
        cmp        eax,'z'
        ja         failure
        cmp        eax,'a'
        jae        success
        cmp        eax,'9'
        jbe        success
        cmp        eax,'A'
        jb         failure
        cmp        eax,'Z'
        ja         failure
success:
        inc        esi
        jmp        top_loop
failure:
        xor        eax,eax
        jmp        exitThisBlock
done:
        mov        eax,1
exitThisBlock:
        ret
    }

Results:

Code:
 lcX = 1.762 to 1.777
@lcX = 3.103 to 3.144

Best regards,
Rick C. Hodgin
 
>lcX = 1.762 to 1.777
>@lcX = 3.103 to 3.144

Simple reasoning, if you folloewed, what I said about copying the string to a c 0-terminated string:
lcX: One way copying, only need to copy for c to handle
@lcX: two way, as the string is passed by ref, what you changed (if) must be put back into lcX. Even if you don't change anything on the C copy of the foxpro string, the DLL call copies that back.

What you measure here mostly is the time needed to copy forth (and back) the string.

With An FLL instead of DLL that might change, even though the FoxTools.FLL proved to be unefficient, that's may just the implementation of the specific StrFilter function.

With an FLL you can define to pass in a parameter by Ref, then you get a Locator struct, of which you can indirectly receive a char*. I'm on it, but the function I have so far causes a strange execpetion after it's 1024th call!?

Code:
#include "stdafx.h"
#include "pro_ext.h"

using namespace std;

void IsAlphaNumeric(ParamBlk FAR *parm)
{
  Locator loc;
  Value val;
  char c;
  unsigned int i;
  int retval;
  char* cString;

  _FindVar(parm->p[0].loc.l_NTI, 0, &loc);
  _Load(&loc, &val);

  if (val.ev_type != 'C')
   {
      _Error(9); // "Data type mismatch"
   }

  // lock the handle
  _HLock(val.ev_handle);

  // convert handle to pointer
  cString = (char*)_HandToPtr(val.ev_handle);

  retval=1;
  // Iterate until we're done or find a non-matching character
  for (i=0;i<val.ev_length;i++)
      {
	c = (cString)[i];
        if ((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c > '9') && (c != ' '))
           { 
               retval=0;        // Failure
               i = val.ev_length;
           }
      }

  // unlock the handle
  _HUnLock(val.ev_handle);
  _RetLogical(retval);
}

FoxInfo myFoxInfo[] = 
{
	{"ISALPHANUMERIC", (FPFI) IsAlphaNumeric, 1, "R"},
};

#ifdef __cplusplus
extern "C"
#endif
FoxTable _FoxTable = {
(FoxTable *)0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};
Bye, Olaf.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top