Page 1 of 1
[Solved] Make string numeric (not Val)
Posted: Thu Mar 04, 2021 9:11 am
by BarryG
Why doesn't this return "123"? I've been trying for an hour, so it's time to ask here.
(Also, I need this code instead of For/Next or While/Wend, because they're too slow for large strings).
Code: Select all
Procedure.s Numeric(text$)
#SOC=SizeOf(Character)
*old.Character=@text$
*new.Character=*old
While *old\c
If *old\c>47 And *old\c<58 ; 0-9, so keep them.
*new\c=*old\c
EndIf
*old+#SOC
Wend
*new\c=0
new$=PeekS(*new)
;FreeMemory(*new)
ProcedureReturn new$
EndProcedure
Debug Numeric("a1b2c3") ; Want: 123
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 9:28 am
by Marc56us
Hi Barry,
I never really understood pointers very well, so I can't help here.
But in case you want another method, here's a little RegEx
Code: Select all
EnableExplicit
Procedure.s Numeric(text$)
If CreateRegularExpression(0, "[^\d]")
ProcedureReturn ReplaceRegularExpression(0, text$, "")
Else
ProcedureReturn RegularExpressionError()
EndIf
EndProcedure
Debug Numeric("a1b2c3") ; return 123
End

Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 9:30 am
by #NULL
You are not incrementing *new, only writing a 0-char as the first character. And reading and writing from/to the same string, is this intentional?
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 9:37 am
by RASHAD
Code: Select all
Procedure.s Numeric(text$)
#SOC=SizeOf(Character)
*old.Character=@text$
*new.Character=*old
While *old\c
If *old\c>47 And *old\c<58 ; 0-9, so keep them.
new$ = new$ + Chr(*old\c)
EndIf
*old+#SOC
Wend
;*new\c=0
;new$=PeekS(*new)
;FreeMemory(*new)
ProcedureReturn new$
EndProcedure
Debug Numeric("a1b2c3") ; Want: 123
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 9:37 am
by BarryG
Marc56us, that's a good way to do it (thanks!) but I also need to learn why my code isn't doing it.
Rashad, new$+Chr() is too slow for large strings (PureBasic limitation). The character approach is the best speed.
#NULL, I want to keep the old string and make the new string just the digits, like the Debug line shows.
I tried adding *new+#SOC in various places but I still can't get the desired output.
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 10:16 am
by breeze4me
See the code.
Code: Select all
Procedure.s Numeric(text$)
#SOC=SizeOf(Character)
*old.Character=@text$
*new.Character=*old
While *old\c
If *old\c>47 And *old\c<58 ; 0-9, so keep them.
*new\c=*old\c
*new+#SOC ;move the pointer to the next position.
EndIf
*old+#SOC
Wend
*new\c=0
new$=PeekS(@text$) ;read from the first byte.
;FreeMemory(*new)
ProcedureReturn new$
EndProcedure
Debug Numeric("a1b2c3") ; Want: 123
Code: Select all
Procedure.s Numeric(text$)
#SOC=SizeOf(Character)
*old.Character=@text$
*new.Character=*old
While *old\c
If *old\c>47 And *old\c<58 ; 0-9, so keep them.
*new\c=*old\c
*new+#SOC
EndIf
*old+#SOC
Wend
*new\c=0
ProcedureReturn text$
EndProcedure
Debug Numeric("a1b2c3") ; Want: 123
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 10:43 am
by AZJIO
Didn't check thoroughly
Code: Select all
EnableExplicit
Procedure StrToArrLetter(Array Arr.c(1), String$)
Protected LenStr, i
LenStr = Len(String$)
If LenStr
ReDim Arr(LenStr - 1)
PokeS(Arr(), String$, -1, #PB_String_NoZero)
EndIf
ProcedureReturn
EndProcedure
Procedure.s Numeric(String$)
Protected c, i
Protected Dim Arr.c(0)
StrToArrLetter(Arr(), String$)
c=0
For i=0 To ArraySize(Arr())
Select Arr(i)
Case 47 To 58
Arr(c) = Arr(i)
c+1
EndSelect
Next
ProcedureReturn PeekS(Arr(), c)
EndProcedure
Debug Numeric("a1b2c3")
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 11:21 am
by IdeasVacuum
Looks like breeze4me has the fastest code. It also works with characters outside the ASCII range.
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 12:11 pm
by BarryG
Yep, I ended up using breeze4me's second code. Thanks for the lesson, and for everyone's contributions.
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 12:47 pm
by Josh
breeze4me wrote:
Code: Select all
If *old\c>47 And *old\c<58 ; 0-9, so keep them.
Just a suggestion for better readability:
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 1:08 pm
by BarryG
Hi Josh, yeah I know about that but I always use one test (>) instead of two (=>) for speed.
Re: Make string numeric (not Val)
Posted: Thu Mar 04, 2021 2:54 pm
by AZJIO
IdeasVacuum wrote:Looks like breeze4me has the fastest code. It also works with characters outside the ASCII range.
Checked the search for the LF character by copying 20,000 lines to the clipboard. My code executed in 207-210 ms, and the code from the author of breeze4me completed in 240-250 ms.
Code: Select all
EnableExplicit
#SOC=SizeOf(Character)
Procedure CountStr(text$)
Protected c=1, *old.Character=@text$
; text$ = RTrim( LTrim(text$, #LF$), #LF$)
While *old\c
If *old\c=10 ; LF
c+1
EndIf
*old+#SOC
Wend
ProcedureReturn c
EndProcedure
; Debug CountStr(GetClipboardText())
Procedure StrToArrLetter(Array Arr.c(1), String$)
Protected LenStr, i
LenStr = Len(String$)
If LenStr
ReDim Arr(LenStr - 1)
PokeS(Arr(), String$, -1, #PB_String_NoZero)
EndIf
ProcedureReturn
EndProcedure
Procedure CountStr1(String$)
Protected c, i
Protected Dim Arr.c(0)
StrToArrLetter(Arr(), String$)
c=1
For i=0 To ArraySize(Arr())
If Arr(i) = 10
c+1
EndIf
Next
ProcedureReturn c
EndProcedure
Define Count, StartTime, Time.s
StartTime=ElapsedMilliseconds()
Count = CountStr(GetClipboardText())
Time = "Time = " + Str(ElapsedMilliseconds()-StartTime) + " ms"
; Debug GetClipboardText()
Debug Time
Debug Count
; это жутко медленно, 14 сек для 20000 строк, против мгновения в предыдущих примерах
Procedure CountStr2(str$)
Protected Pos=0, c
str$ = RTrim( LTrim(str$, #LF$), #LF$)
c=0
Repeat
c+1
Pos = FindString(str$, #LF$, Pos+1)
Until Not Pos
ProcedureReturn c
EndProcedure
Re: Make string numeric (not Val)
Posted: Fri Mar 05, 2021 5:20 am
by breeze4me
AZJIO wrote:IdeasVacuum wrote:Looks like breeze4me has the fastest code. It also works with characters outside the ASCII range.
Checked the search for the LF character by copying 20,000 lines to the clipboard. My code executed in 207-210 ms, and the code from the author of breeze4me completed in 240-250 ms.
The debugger must be turned off.
BTW, the CountString() function is much faster.
Code: Select all
EnableExplicit
DisableDebugger
#SOC=SizeOf(Character)
Procedure CountStr(text$)
Protected c=0, *old.Character=@text$
; text$ = RTrim( LTrim(text$, #LF$), #LF$)
While *old\c
If *old\c=10 ; LF
c+1
EndIf
*old+#SOC
Wend
ProcedureReturn c
EndProcedure
Procedure StrToArrLetter(Array Arr.c(1), String$)
Protected LenStr, i
LenStr = Len(String$)
If LenStr
ReDim Arr(LenStr - 1)
PokeS(Arr(), String$, -1, #PB_String_NoZero)
EndIf
ProcedureReturn
EndProcedure
Procedure CountStr1(String$)
Protected c, i
Protected Dim Arr.c(0)
StrToArrLetter(Arr(), String$)
c=0
For i=0 To ArraySize(Arr())
If Arr(i) = 10
c+1
EndIf
Next
ProcedureReturn c
EndProcedure
Define i, t.s
Define Count1, StartTime, Time1.s
Define Count2, Time2.s, Count3, Time3.s
Define *Buffer = AllocateMemory(10000000)
Define *Pointer = *Buffer
Define s200.s = Space(200) + #CRLF$
For i = 1 To 20000
CopyMemoryString(s200, @*Pointer)
Next
t = PeekS(*Buffer)
StartTime=ElapsedMilliseconds()
Count1 = CountStr(t)
Time1 = "Time = " + Str(ElapsedMilliseconds()-StartTime) + " ms"
StartTime=ElapsedMilliseconds()
Count2 = CountStr1(t)
Time2 = "Time = " + Str(ElapsedMilliseconds()-StartTime) + " ms"
StartTime=ElapsedMilliseconds()
Count3 = CountString(t, #LF$)
Time3 = "Time = " + Str(ElapsedMilliseconds()-StartTime) + " ms"
; Debug GetClipboardText()
EnableDebugger
Debug Time1
Debug Count1
Debug Time2
Debug Count2
Debug Time3
Debug Count3
Time = 12 ms
20000
Time = 27 ms
20000
Time = 2 ms
20000