Need help with some text functions

I wonder if that was from the days of a 16 bit compiler as I have always understood LONGs to be fastest in 32 bit. When I get time I will do some testing.

thanks Carl for delving into the generated assembly to see why using numeric comparisons is faster than the string comparisons on the case statements.

yes perhaps “technically”. We will all face challenges if/when forced to use Unicode or I guess any other data format where there is no longer a 1:1 correspondence between byte and character.

And so old habits will need to change if/when that time comes and code will need to be re-visited. I guess it depends if you know the format of your data and if you want to take the inevitable performance hit earlier or later.

On the one hand you have the “don’t waste resources coding now for things that may never happen” school and on the other you have “just remember Y2K”.

Of course some users (depending on the languages they are catering for) may already have this requirement and so will already have adjusted their coding practices whereas for others (perhaps mainly English speaking?) this is yet to become an issue.

anyway at the least it is something to be aware of and bear in mind.

Donn you are putting a procedure in the embed that mentions Routines.

See if there is something like

! Start of “Local Procedures”

and if using a local procedure you will also need to put a prototype in a MAP.

I usually put in a MAP just before the procedure’s CODE statement.

But to be honest it is probably easier for a beginner to not have the procedures local - just create separate procedures and if you make them global (there is a check box) then they will be in scope and all should compile without further problems.

1 Like

The sooner we start thinking in terms of 1 char <> 1 byte, and the sooner we start applying that to our code the
a) easier it will be to refactor old code when the time comes and
b) less code will need refactoring when the time comes.

Thanks for sorting out my confusion. I am busy experimenting.

In the meantime I have another solution, based on what Bruce said, in the TakeCompleted section of the form

The DO FullNameCalc is working exactly as I wanted, too.

Everyone who contributed has taught me something, for which I am most grateful. Thank you one and all!

I see that the problem is solved, just want to add one more solution with regular expressions (one of the Clarion implementations could be found here). Below is SortField calculation as Donn described in starting topic.

sSortField                      STRING(6)
re                              TJSRegExp

  re.CreateNew('[^a-zA-Z]', 'g')    !- remove any char except latin letters
  sSortField[1:3] = UPPER(re.Replace(pLastName, ''))
  sSortField[4:6] = LOWER(re.Replace(pFirstName, ''))
  
  MESSAGE(sSortField)
1 Like

You said you wanted to learn Clarion … Being able to create your own Functions and call them is a common need. I’ll repost the code and the MAP you need to do that …

I’ll keep it simple. My chars have 8 bits, just like .KeepChars() works Bruce. I’ll make them local functions, when that works we can make the APP functions.

#1 - In Embed “Local Data - Other Declarations” put the MAP Prototype declarions. In Clarion you must Declare and Define.

   MAP
SortName3x3   PROCEDURE(STRING pFirstName, STRING pLastName),STRING
SortPart4Name PROCEDURE(*STRING pName, BOOL pIsLastName=1, BYTE pLenPart=3),STRING
   END

#2 - In Embed “Local Procedures” define your Procedure. Note here the “Parameters” have the RETURN as a ! comment ()!,STRING because that only goes in the MAP.

SortName3x3 PROCEDURE(STRING pFirstName, STRING pLastName)  !,STRING
    CODE
    RETURN SortPart4Name(pLastName, 1,3) & |
           SortPart4Name(pFirstName,0,3) 

SortPart4Name PROCEDURE(*STRING pName, BOOL pIsLastName=1, BYTE pLenPart=3)  !,STRING
Part STRING(pLenPart)  !<-- Sized as specified
P   BYTE
X   LONG,AUTO
    CODE
    LOOP X=1 TO SIZE(pName)
        CASE pName[X]
        OF   'a' to 'z'      !First as more common                                             
        OROF 'A' to 'Z'
           P += 1
           Part[P]=pName[X]
           IF P=pLenPart THEN BREAK.
        END
    END
    Part=CHOOSE(~pIsLastName,lower(Part),UPPER(Part))
    RETURN Part

#3 - In Embed “Procedure Routines”

NameCalcs ROUTINE
  Cus:FullName=LEFT(CLIP(Cus:FirstName)&' ') & Cus:LastName
  DISPLAY(?Cus:FullName)
  Cus:SortField=SortName3x3(Cus:FirstName,Cus:LastName)
  DISPLAY(?Cus:SortField)    

The code you posted earlier had an extra “)” that would cause a compile error.

image

I’ll toss out this code as possibly better for non A-Z alphabets. I don’t deal with international issues so YMMV. Read the docs on IsLower(). Edit: change *STRING to no * because PName gets changed.

SortPart4Name PROCEDURE(STRING pName, BOOL pIsLastName=1, BYTE pLenPart=3)  !,STRING
Part STRING(pLenPart)  !<-- Sized as specified
P   BYTE
X   LONG,AUTO
    CODE
    pName=CHOOSE(~pIsLastName,lower(pName),UPPER(pName))
    LOOP X=1 TO SIZE(pName)
        IF ( pIsLastName AND isUPPER(pName[X])) |
        OR (~pIsLastName AND ISlower(pName[X])) THEN 
           P += 1
           Part[P]=pName[X]
           IF P=pLenPart THEN BREAK.
        END
    END
    RETURN Part

Hello again Donn

to be honest this is something of a variation on what I gave you early on (just scroll up) when I posted :

st stringTheory
sortField stringTheory

(snip)

st.setValue(upper(LastName))
st.keepChars(‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’)
sortField.setvalue(st.slice(1,3))
sortField.setLength(3) ! in case last name < 3 chars

st.setValue(lower(FirstName))
st.keepChars(‘abcdefghijklmnopqrstuvwxyz’)
sortField.append(st.slice(1,3))

mySortField = sortField.getValue()   

However there is a bug in what you have now implemented in that it does not cater for the possibility of a last name with fewer than 3 characters. This was the purpose of this line above:

sortField.setLength(3) ! in case last name < 3 chars

just another thought Donn as there are always lots of ways to do these things.

where you currently have

cus:SortField = stL.sub(1,3) & stF.sub(1,3)

you could fix the problem of a short Last Name by using string slicing on the LHS as Mike did in his example:

cus:SortField[1 : 3] = stL.sub(1,3)
cus:SortField[4 : 6] = stF.sub(1,3)

note you could also have used stL.slice(1,3) or even just stL.getvalue()

in fact you could have done it with just one ST object:

st stringTheory

(snip)

st.setValue(upper(cus:LastName))
st.keepChars(‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’)
cus:SortField[1 : 3] = st.slice(1,3)

st.setValue(lower(cus:FirstName))
st.keepChars(‘abcdefghijklmnopqrstuvwxyz’)
cus:SortField[4 : 6] = st.slice(1,3)

again you could have just used st.getValue() instead of st.slice(1,3) or st.sub(1,3) but I think the intention is probably clearer using slice or sub.

re Carl’s comment about learning Clarion, I agree you will learn more doing it by “long hand” and I recommend you get Carl’s version working for you and make sure you study and understand it as you will definitely learn more.

once you have that knowledge it will also help you understand that string classes like ST can save you a lot of work.

You guys have all taught me so much. I’m really grateful. It has given me the confidence to continue with my project. I will pay it forward at some point.

Thank you to Carl, Geoff, Mike, Bruce, Richard, Douglas, Jeff and Sean. You have each taught me stuff, and encouraged me tremendously, even when I got things wrong.

1 Like

You learn more by fixing a fkuc up than any other time :laughing: … and the stress usually means you remember it for next time :upside_down_face:

1 Like

That is absolutely true, and funny too :rofl: I have made plenty of mistakes and you guys have been gracious enough to help me fix them and learn.

funny I just came across this earlier conversation from three and a half years ago and noticed the above.

at the time I had endorsed this without qualification:

but re-reading it now the closing bracket for the left seems to be misplaced. I think this is what was intended:

FullName=LEFT(CLIP(FirstName)&' ' & LastName)   !Left() incase First Name Blank

the reason for this is that doing a left on a blank string does not alter it so if FirstName was blank, the way it was you would still have a space at the start.

@DonnEdwards you might want to check your code and alter it if you used that.

And while I am here, from the current ST version 3.70 on, where I said:

st.keepChars(‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’)

you can now simply say

st.keepCharRanges(‘A-Z’)

and ditto for

st.keepChars(‘abcdefghijklmnopqrstuvwxyz’)

you can now say

st.keepCharRanges(‘a-z’)

Looks like case insensitive RegEx (StrPos/match) talk.

@DonnEdwards doesnt mention if using ABC, but if so, that code can go into the global embeds.

Field level validation
Table/Filename:?
Field: Fullname
Create Source embed with Priority 4000
Add the code
FullName = left(clip(FirstName)&’ ') & LastName

Then everytime the record is updated, the code runs. This is handy when you have a one or more procedures (forms/processes/source) updating the records because the code to update and maintain Fullname is stored once globally and performed whenever any procedure updates that file, eliminating any risks of cut n paste code errors creeping in.

yes a little, but character ranges so it is case sensitive. But you could say

st.keepCharRanges(‘a-zA-Z’)

which will then keep both lower and upper case letters (and get rid of everything else)

hey! :grinning: I’m sure you really meant:

FullName = left(clip(FirstName)&’ ’ & LastName)

that sounds a bit like dictionary (client side) triggers

cheers for now

So ST is using regexes then. I saw an entry in the docs on regex but no methods other than this housekeeping one and possibly a few others? Are any of the methods that use regex labelled?

Yeah the field level validation is like a dct trigger but working at the field level. Its where the dct field validation code appears like the dct must be in file and the others seen on that tab in the dct.

Hi Richard

ST does have some Regex functionality but I had not thought of these new Character Range methods (introduced in version 3.70) in that light. I guess you could think of the character ranges as either having been borrowed from regex or alternatively implementing a very small subset of regex, but really it was just an abbreviation as I found typing out all the characters tedious and potentially error-prone and the alternative of writing a loop dealing with one character at a time seemed less than ideal.

I see you have started another thread which is a good idea as we are having thread drift here and being off-topic so I will answer over there instead:

1 Like