[Solved] Make string numeric (not Val)

Windows specific forum
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

[Solved] Make string numeric (not Val)

Post 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
Last edited by BarryG on Thu Mar 04, 2021 12:17 pm, edited 1 time in total.
Marc56us
Addict
Addict
Posts: 1477
Joined: Sat Feb 08, 2014 3:26 pm

Re: Make string numeric (not Val)

Post 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:
Last edited by Marc56us on Thu Mar 04, 2021 9:30 am, edited 1 time in total.
#NULL
Addict
Addict
Posts: 1440
Joined: Thu Aug 30, 2007 11:54 pm
Location: right here

Re: Make string numeric (not Val)

Post 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?
RASHAD
PureBasic Expert
PureBasic Expert
Posts: 4636
Joined: Sun Apr 12, 2009 6:27 am

Re: Make string numeric (not Val)

Post 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
Egypt my love
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: Make string numeric (not Val)

Post 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.
breeze4me
Enthusiast
Enthusiast
Posts: 511
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Make string numeric (not Val)

Post 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
AZJIO
Addict
Addict
Posts: 1318
Joined: Sun May 14, 2017 1:48 am

Re: Make string numeric (not Val)

Post 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")
IdeasVacuum
Always Here
Always Here
Posts: 6425
Joined: Fri Oct 23, 2009 2:33 am
Location: Wales, UK
Contact:

Re: Make string numeric (not Val)

Post by IdeasVacuum »

Looks like breeze4me has the fastest code. It also works with characters outside the ASCII range.
IdeasVacuum
If it sounds simple, you have not grasped the complexity.
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: Make string numeric (not Val)

Post by BarryG »

Yep, I ended up using breeze4me's second code. Thanks for the lesson, and for everyone's contributions.
User avatar
Josh
Addict
Addict
Posts: 1183
Joined: Sat Feb 13, 2010 3:45 pm

Re: Make string numeric (not Val)

Post 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'
sorry for my bad english
BarryG
Addict
Addict
Posts: 3292
Joined: Thu Apr 18, 2019 8:17 am

Re: Make string numeric (not Val)

Post by BarryG »

Hi Josh, yeah I know about that but I always use one test (>) instead of two (=>) for speed.
AZJIO
Addict
Addict
Posts: 1318
Joined: Sun May 14, 2017 1:48 am

Re: Make string numeric (not Val)

Post 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
breeze4me
Enthusiast
Enthusiast
Posts: 511
Joined: Thu Mar 09, 2006 9:24 am
Location: S. Kor

Re: Make string numeric (not Val)

Post 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
Post Reply