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
:wink:

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:

Code: Select all

If *old\c>='0' And *old\c<='9'

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. :wink:

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