|
|
| Author |
Message |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 20 July 2006, 9:22 AM Post subject: |
|
|
Don,
| Quote: |
The second test is entirely superfluous. Since c is a Byte value, its value is never less than zero. After subtracting 48, characters preceding the digit zero will result in values from &Hce to &Hff, all of which are larger than 9. Characters following the digit 9 will all yield values above 9 after subtraction. Hence the first test suffices.
|
I'm not smart enough to have thought of that Thanks for 'enlightening' me.
As for the if/then/else thingy, just more 'enlightenment'
Vic |
|
| Back to top |
|
 |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 20 July 2006, 9:36 AM Post subject: |
|
|
Don,
| Quote: |
If you use tabs in your code it works out better. The problem is that you can't keyboard a tab in the edit box. You can, however, copy and paste a tab or a block of code containing tabs.
|
I always use tabs in my code. Tabs have been a bugaboo for me since the '80s when I started writing code. I have tab spacing set to two in my IDE.
Vic |
|
| Back to top |
|
 |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 20 July 2006, 9:42 AM Post subject: |
|
|
Don,
I incorporated your latest suggestions.
| Code: |
Sub CS2Int(ByRef istr as string, _
ByRef toint as integer, _
ByRef flag as byte)
'this subroutine convertes the numeric value in istr into the integer
'toint. The only allowed characters are minus and 0 - 9. If istr represents
'a negative number then the minus sign must be the first character in the
'string.
'The subroutine returns:
' 0 in flag if the conversion is successful,
' 1 in flag if it incounters a non-number in istr
' 2 in flag if number exceeds the value of an integer
' 3 if istr contains NO data
Const MINUS as byte = 45
Const MAXINT as Long = 32767
Dim neg as boolean
Dim l as Long
Dim d as integer
Dim ctr as integer
Dim rstr as string
Dim c as byte
flag = 0 'assume success
d = 1 'start of string
l = 0 'no value yet
rstr = Trim(istr) 'dump any spaces
If Len(rstr) = 0 Then 'sent a nil string
flag = 3 'flag nothing in string
toint = 0 'send back 0
Exit Sub 'go home
End If
c = Asc(rstr,1) 'look at first character
neg = (c = MINUS) 'is it a minus sign
If neg Then 'if so
d = 2 'skip over it
End If
For ctr = d To Len(rstr) 'for all further characters
c = Asc(rstr, ctr) - 48 'make each numneric
if (c > 9) Then 'is it 0 to 9?
flag = 1 'if not set error flag
Exit For 'get out
End If
l = 10 * l + CLng(c)
If l > MAXINT then 'got too big??
flag = 2 'flag it
Exit For 'get out
End If
Next
If CBool(flag) Then 'other errors?
l = 0 'return 0
End If
If neg Then 'was number negative?
l = -l 'negate our results
End If
toint = CInt(l) 'send it home
End Sub 'CS2Int
|
I hope someone finds it useful
BTW: everything was tab aligned in the IDE.
Vic |
|
| Back to top |
|
 |
spamiam
Joined: 13 Nov 2005
Posts: 689
|
|
Posted: 20 July 2006, 13:15 PM Post subject: |
|
|
Vic, it looks slick and quite robust.
I just wonder if the line
could be omitted in some way. It is a good check to do, but if it is a numerical string with trailing spaces, then you could deal with this in the loop where you decode the number.
To do this, check the first character that should be a digit. If it is, then you have at least one good digit and you can set a "good data" flag to true.
Then you read out the digits the way you uusally do. At exit, if you have the error flag set (i.e. abnormal completion of the character interpretation), but also have the "good data" flag, then use the good data and clear the abnormal completion flag and exit.
I have to go now, but I will throw together my modification for you later this AM. Real work calls.
-Tony |
|
| Back to top |
|
 |
spamiam
Joined: 13 Nov 2005
Posts: 689
|
|
Posted: 20 July 2006, 15:15 PM Post subject: |
|
|
Here is the way to avoid the need to use the TRIM function. Functionally it will work the same as your original code, but only tests for a trailing space if it actually encounters a non-numerical character. It ought to work a little faster than ALWAYS testing all the characters for being a space.
| Code: |
'Do not use the TRIM function
Const SPACE as Byte = 240
'ascii value of a space less 48, wrapped around 256
'32+256-48 = 240 [be sure this is not "off by 1"]
For ctr = d To Len(rstr) 'for all further characters
c = Asc(rstr, ctr) - 48 'make each numneric
if (c > 9) Then 'is it 0 to 9?
If (c=SPACE) Then
Exit For 'exit without error
Else
flag = 1 'set error flag
Exit For 'get out
End If
End If
l = 10 * l + CLng(c)
If l > MAXINT then 'got too big??
flag = 2 'flag it
Exit For 'get out
End If
Next |
|
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2593
Location: Portland, OR
|
|
Posted: 20 July 2006, 15:23 PM Post subject: |
|
|
| Quote: | | I have tab spacing set to two in my IDE. |
That's why the code has unusual spacing. The standard in the Unix environment has been that tab stops are every 8 spaces. I have always thought that this was entirely too much and have used 4 spaces for many years. The forum code seems to use 3 spaces, as can be seen in the test code below.
| Code: |
1 2 3 4
1234567890123456789012345678901234567890
1 tab
2 tabs
3 tabs
4 tabs
|
I suspect that the only way to solve this is to expand tabs to spaces before pasting the code. That would preserve the spacing that you see in your editing environment, irrespective of what the tabstop settings are. |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2593
Location: Portland, OR
|
|
Posted: 20 July 2006, 15:32 PM Post subject: |
|
|
| Quote: | | Here is the way to avoid the need to use the TRIM function. |
One advantage to not using Trim() is that the string can then be passed ByVal, and the source string will not be changed.
| Quote: | | Functionally it will work the same as your original code[...] |
As I read it, it does not correctly handle a string with invalid characters after the first trailing space, e.g. " 123 xxx". My original post contained an oft-used function skipWhite(), reproduced below, that could be employed to good advantage. Then, when processing is done, if the index is past the end of the string, then the string contained only acceptable characters.
| Code: | '
'' skipWhite
'
' Advance an index past spaces/tabs in a string, limited by the
' given length. The index of the first non-whitespace character
' is returned.
'
Private Function skipWhite(ByVal str as String, ByVal slen as Integer, _
ByVal idx as Integer) as Integer
Do While (idx <= slen)
Dim c as Byte
c = Asc(str, idx)
If ((c <> &H20) And (c <> &H09)) Then
Exit Do
End If
idx = idx + 1
Loop
skipWhite = idx
End Function |
|
|
| Back to top |
|
 |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 20 July 2006, 17:30 PM Post subject: |
|
|
I see little advantage to adding another layer of complexity to the code. The system function TRIM gets the job done. I thought that is what the system library is for. CS2Int is free code and anyone can modify it to their heart's content.
I have a question. In this snippet:
| Code: |
If ((c <> &H20) And (c <> &H09)) Then
|
Is there an advantage to using the hexadecimal values? What advantage is gained over using 32 and 9? Just curious
Vic |
|
| Back to top |
|
 |
spamiam
Joined: 13 Nov 2005
Posts: 689
|
|
Posted: 20 July 2006, 18:16 PM Post subject: |
|
|
| Quote: | | As I read it, it does not correctly handle a string with invalid characters after the first trailing space, e.g. " 123 xxx". |
Well, I suppose that depends on the definition of "correctly". I had considered this issue as I suggested my modification.
As Vic wrote the code, any non-numeric characters in the middle of a set of numeric characters, or non-space, non-numeric characters at the end will trigger an error condition.
I would agree with this. A space in the middle of a string representing a single number would be highly suspect. "123 456" would seem to me to be two separate numbers rather one number of the value 123456. In this case, I would suppose that the most correct of the possible "valid" numerical answer is 123, and this is what my modification would return.
Vic's version is probably most corrrect by just generating an error condition and leaving it at that. So, I stand corrected. Leave Vic's code the way he wrote it.
I consider it bad form to modify the source string. I think it would be "best" to pass the string by val, but it does consume stack space to duplicate the string.
Responding to Vic's question re: hex constants adding any efficiency. I doubt that the HEX values are any more efficient than DEC for the compiler. I use hex if the number is actually a binary bit pattern, and not a numerical value as I am using it. I use Dec if it is a numerical value, usually.
It almost seems as if Don was breaking his own rule about "magic numbers". I always have to look up ascii values. I can remember only a couple of characters off the top of my head. I think that these should have been named constants. That is what I did with the SPACE value in my version of the code.
Of course, as Vic said that this is just suggested code, and feel free to modify it to fit your individual needs. But.... someone may look at this code to paste into their own program. As such it should be as efficient as possible. Even more, it should serve as a coding tutorial. It should demonstrate "best coding practices", whatever they are.
I think the value of this discussion is the discussion istelf, not the actual code that is generated! I know that I have a lot to learn about "good" coding!!!! Like I said, I never took a course, although I actually used to tutor people in Pascal.
It was pretty funny. I was tutoring people in Pascal eventhough I did not know Pascal syntax at all. It worked out really well. Most of the time the students knew HOW to write the code, they just did not know WHAT to write. They needed help on the algorithms. THis worked out really well. I was able to teach them in a non-language specific manner, and they then had to write their own code. If I had more Pascal abilities, I would have led them further down the line until they had a final product after their tutoring. I liked it better this way. I think they got more out of it too. They would come back a little while later and we would look at the code and the output and then see what it was actually doing compared to what we had expected based on the algorithm. I really enjoyed that job.
-Tony |
|
| Back to top |
|
 |
stevech
Joined: 23 Feb 2006
Posts: 688
|
|
Posted: 20 July 2006, 18:23 PM Post subject: |
|
|
A ZBasic version of sscanf() as in C might be useful to everyone. One could transliterate the public domain C code for this handy function.
sscanf() handles longs, ints, signed/unsigned, floats and strings. Often sscanf() is written assembly language for the sake of speed, for microprocessor libraries.
sscanf() is arguably an overkill for just Cint() of a string.
A ZBasic translation of the C library atoi() and atol() are applicable. |
|
| Back to top |
|
 |
dkinzer Site Admin
Joined: 03 Sep 2005
Posts: 2593
Location: Portland, OR
|
|
Posted: 20 July 2006, 20:20 PM Post subject: |
|
|
| victorf wrote: | | Is there an advantage to using the hexadecimal values? What advantage is gained over using 32 and 9? |
There is no advantage unless you think of character values in terms of hexadecimal. I know the hex values for most of the ASCII character set but I don't know the decimal equivalents. It's somewhat like thinking in French vs. English.
Regarding Tony's comment about magic numbers - ASCII character values are well known and are thus less "magic". If the purpose of a routine is to process a character string, a programmer with even a modicum of experience will recognize what's being done even if they have to consult a chart to figure out the meaning of the specific values. That said, it may still be useful to define constants for the character values for the reader who doesn't know the ASCII table.
Similarly, if your routine is calculating the area of a circle, using the magic number 3.14159 is unlikely to cause anyone to ponder why that particular value is being used.
I suppose a general rule would be to define constants for such values if doing so is likely to make the code more readable. |
|
| Back to top |
|
 |
victorf
Joined: 01 Jan 2006
Posts: 342
Location: Schenectady, New York
|
|
Posted: 20 July 2006, 20:31 PM Post subject: |
|
|
I normally define constants for these 'magic numbers'. I always make them all caps so they stand out. See MAXINT and MINUS in my code. I'm a decimal guy, but keep a chart handy just in case.
Vic |
|
| Back to top |
|
 |
|