Android Question I need help with RegEx to complete a B4A/B4I Ping library

Sergio Haurat

Active Member
Licensed User
Longtime User
This project is based on a response by @NJDude in an old post, that I can't find at the moment. When I finish it, I will add this library shared by @Erel for B4I.

Every segment in bold and red should be returned to a variable. Currently, I'm using IndexOf, but I think it would be better to do it with RegEx. Below is the text and a current example of how I get the host and IP with IndexOf:

PING www.google.com (142.251.133.4) 56(84) bytes of data.
64 bytes from eze10s01-in-f4.1e100.net (142.251.133.4): icmp_seq=1 ttl=116 time=17.5 ms

--- www.google.com ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4011ms
rtt min/avg/max/mdev = 17.570/23.754/33.455/6.264 ms

B4X:
'First line
toHost = lneResult.SubString2(lneResult.IndexOf(" "), lneResult.IndexOf(" (")).Trim
toIP = lneResult.SubString2(lneResult.IndexOf("(") + 1, lneResult.IndexOf(")")).Trim

'Ping responses

Dim intSeq As Byte = lneResult.SubString2(lneResult.IndexOf("icmp_seq=") + 9, lneResult.IndexOf("ttl")).Trim.As (Byte)
Dim mapSeq As Map
mapSeq.Initialize
mapSeq.Put("from", lneResult.SubString2(lneResult.IndexOf("from ") + 5, lneResult.IndexOf("(")).trim)
mapSeq.Put("ttl", lneResult.SubString2(lneResult.IndexOf("ttl=") + 4, lneResult.IndexOf("time")).trim)
mapSeq.Put("time", lneResult.SubString2(lneResult.IndexOf("time=") + 5, lneResult.IndexOf("ms")).trim)
icmp_seq.Put(intSeq, mapSeq)

The code so far is as follows; I only have the last 2 lines of the statistics left.

B4X:
Sub Process_Globals
  Type tpePing (Success As Boolean, toHost As String, toIP As String, mapResponse As Map, mapStatistics As Map, Msg As String)
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Dim pingResult As tpePing = Ping("www.google.com", 5, 100)
    Log(pingResult.Msg)
End Sub

Public Sub Ping(URL As String, Attempts As Int, Timeout As Int) As tpePing
    Dim pingResult As tpePing
    pingResult.Initialize
    If Attempts = 0 Then
        pingResult.Msg = "Attempts must be greater than or equal to 1"
    Else
        Dim sb As StringBuilder
        Dim p As Phone
        sb.Initialize
        p.Shell("ping -c" & Attempts & " -W" & Timeout & " -v " & URL, Null, sb, Null)
        If sb.Length = 0 Then
            pingResult.Msg = "Host unreachable"
        Else
            pingResult.Success = True
            pingResult.Msg = sb.ToString
            Dim aryResult() As String = Regex.Split(CRLF, pingResult.Msg)
            Log(sb.ToString)
            Dim blnIsFirstLine As Boolean = False
            Dim blnStats As Boolean = False
            Dim bteLine As Byte = 0
            Dim icmp_seq As Map
            icmp_seq.Initialize
            For Each lneResult As String In aryResult
                If Not (blnIsFirstLine) Then
                    blnIsFirstLine = True
                    pingResult.toHost = lneResult.SubString2(lneResult.IndexOf(" "), lneResult.IndexOf(" (")).Trim
                    pingResult.toIP = lneResult.SubString2(lneResult.IndexOf("(") + 1, lneResult.IndexOf(")")).Trim
                Else If Not (blnStats)  Then
                    If lneResult <> "" And lneResult.IndexOf("ping statistics") = -1 Then
                        Dim intSeq As Byte = lneResult.SubString2(lneResult.IndexOf("icmp_seq=") + 9, lneResult.IndexOf("ttl")).Trim.As (Byte)
                        Dim mapSeq As Map
                        mapSeq.Initialize
                        mapSeq.Put("from", lneResult.SubString2(lneResult.IndexOf("from ") + 5, lneResult.IndexOf("(")).Trim)
                        mapSeq.Put("ttl", lneResult.SubString2(lneResult.IndexOf("ttl=") + 4, lneResult.IndexOf("time")).Trim)
                        mapSeq.Put("time", lneResult.SubString2(lneResult.IndexOf("time=") + 5, lneResult.IndexOf("ms")).Trim)
                        icmp_seq.Put(intSeq, mapSeq)
                    End If
                Else If blnStats Then
                    Dim mapStats As Map
                    mapStats.Initialize
                    pingResult.mapStatistics.Initialize
                    If bteLine = 0 Then
                        mapStats.Put("packets transmitted", lneResult.SubString2(0, lneResult.IndexOf(" packets")).Trim)
                        mapStats.Put("received", lneResult.SubString2(lneResult.IndexOf("transmitted, ") + 13, lneResult.IndexOf("received")).Trim)
                        mapStats.Put("packet loss", lneResult.SubString2(lneResult.IndexOf("received, ") + 10, lneResult.IndexOf("%")).Trim)
                        mapStats.Put("time", lneResult.SubString2(lneResult.IndexOf("time ") + 5, lneResult.IndexOf("ms")).Trim)
                        pingResult.mapStatistics.Put("StatsLine1", mapStats)
                    Else If bteLine = 1 Then
                        Dim strDataSegment As String = lneResult.SubString2(lneResult.IndexOf("= ") + 2, lneResult.IndexOf(" ms")).Trim
                        Dim aryDataSegment() As String =  Regex.Split("/", strDataSegment)
                        mapStats.Put("packets transmitted", aryDataSegment(0))
                        mapStats.Put("received", aryDataSegment(1))
                        mapStats.Put("packet loss", aryDataSegment(2))
                        mapStats.Put("time", aryDataSegment(3))
                        pingResult.mapStatistics.Put("StatsLine2", mapStats)
                    End If
                    bteLine = bteLine + 1
                End If
                If Not (blnStats) Then
                    'Skip --- DEST_HOST ping statistics --- string and add map with all ping's result to pingResult
                    If lneResult.IndexOf("ping statistics") > 0 Then
                        pingResult.mapResponse = icmp_seq
                        blnStats = True
                    End If
                End If
            Next
        End If
    End If
    Return pingResult
End Sub
 
Last edited:
Solution
Find the solution, this is the correct line.
Dim regexICMPSeq As String = ".* from ([^ ]+) \(([^)]+)\): icmp_seq=([0-9]+) ttl=([0-9]+) time=([0-9.]+) ms"

The final code is the following
B4X:
Public Sub PingRegEx(URL As String, Attempts As Int, Timeout As Int) As tpePing
    Dim pingResult As tpePing
    pingResult.Initialize
    If Attempts = 0 Then
        pingResult.Msg = "Attempts must be greater than or equal to 1"
    Else
        Dim sb As StringBuilder
        Dim p As Phone
        sb.Initialize
        p.Shell("ping -c" & Attempts & " -W" & Timeout & " -v " & URL, Null, sb, Null)
        If sb.Length = 0 Then
            pingResult.Msg = "Host unreachable"
        Else
            pingResult.Success = True...

JohnC

Expert
Licensed User
Longtime User
ChatGPT says...

To improve your code using Regular Expressions (RegEx), let's create the necessary RegEx patterns to extract the required data segments from the ping output.

Here's how you can define and use RegEx patterns to capture the segments in bold and red from your example:


  1. Host and IP in the first line:
  2. Ping response details:
    • icmp_seq=1
    • ttl=116
    • time=17.5 ms
  3. Ping statistics:
    • packets transmitted = 5
    • received = 5
    • time = 4011ms
    • rtt min/avg/max/mdev = 17.570/23.754/33.455/6.264 ms
Here is how you can use RegEx to extract these details:
B4X:
Sub Process_Globals
  Type tpePing (Success As Boolean, toHost As String, toIP As String, mapResponse As Map, mapStatistics As Map, Msg As String)
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Dim pingResult As tpePing = Ping("www.google.com", 5, 100)
    Log(pingResult.Msg)
End Sub

Public Sub Ping(URL As String, Attempts As Int, Timeout As Int) As tpePing
    Dim pingResult As tpePing
    pingResult.Initialize
    If Attempts = 0 Then
        pingResult.Msg = "Attempts must be greater than or equal to 1"
    Else
        Dim sb As StringBuilder
        Dim p As Phone
        sb.Initialize
        p.Shell("ping -c" & Attempts & " -W" & Timeout & " -v " & URL, Null, sb, Null)
        If sb.Length = 0 Then
            pingResult.Msg = "Host unreachable"
        Else
            pingResult.Success = True
            pingResult.Msg = sb.ToString
            Dim aryResult() As String = Regex.Split(CRLF, pingResult.Msg)
            Log(sb.ToString)
            Dim blnIsFirstLine As Boolean = False
            Dim blnStats As Boolean = False
            Dim bteLine As Byte = 0
            Dim icmp_seq As Map
            icmp_seq.Initialize
            
            ' Regular Expressions
            Dim hostIPRegex As String = "PING (\S+) \(([\d\.]+)\)"
            Dim responseRegex As String = "icmp_seq=(\d+) ttl=(\d+) time=([\d\.]+) ms"
            Dim statsRegex1 As String = "(\d+) packets transmitted, (\d+) received, (\d+)% packet loss, time (\d+)ms"
            Dim statsRegex2 As String = "rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms"
            
            For Each lneResult As String In aryResult
                If Not (blnIsFirstLine) Then
                    blnIsFirstLine = True
                    Dim m As Matcher
                    m = Regex.Matcher(hostIPRegex, lneResult)
                    If m.Find Then
                        pingResult.toHost = m.Group(1)
                        pingResult.toIP = m.Group(2)
                    End If
                Else If Not (blnStats) Then
                    If lneResult <> "" And lneResult.IndexOf("ping statistics") = -1 Then
                        Dim m As Matcher
                        m = Regex.Matcher(responseRegex, lneResult)
                        If m.Find Then
                            Dim intSeq As Byte = m.Group(1).Trim.As(Byte)
                            Dim mapSeq As Map
                            mapSeq.Initialize
                            mapSeq.Put("icmp_seq", m.Group(1).Trim)
                            mapSeq.Put("ttl", m.Group(2).Trim)
                            mapSeq.Put("time", m.Group(3).Trim)
                            icmp_seq.Put(intSeq, mapSeq)
                        End If
                    End If
                Else If blnStats Then
                    Dim m As Matcher
                    If bteLine = 0 Then
                        m = Regex.Matcher(statsRegex1, lneResult)
                        If m.Find Then
                            Dim mapStats As Map
                            mapStats.Initialize
                            mapStats.Put("packets transmitted", m.Group(1))
                            mapStats.Put("received", m.Group(2))
                            mapStats.Put("packet loss", m.Group(3))
                            mapStats.Put("time", m.Group(4))
                            pingResult.mapStatistics.Put("StatsLine1", mapStats)
                        End If
                    Else If bteLine = 1 Then
                        m = Regex.Matcher(statsRegex2, lneResult)
                        If m.Find Then
                            Dim mapStats As Map
                            mapStats.Initialize
                            mapStats.Put("min", m.Group(1))
                            mapStats.Put("avg", m.Group(2))
                            mapStats.Put("max", m.Group(3))
                            mapStats.Put("mdev", m.Group(4))
                            pingResult.mapStatistics.Put("StatsLine2", mapStats)
                        End If
                    End If
                    bteLine = bteLine + 1
                End If
                If Not (blnStats) Then
                    'Skip --- DEST_HOST ping statistics ---
                    If lneResult.IndexOf("ping statistics") > 0 Then
                        pingResult.mapResponse = icmp_seq
                        blnStats = True
                    End If
                End If
            Next
        End If
    End If
    Return pingResult
End Sub
This code uses regular expressions to extract the host and IP from the first line, the details of each ping response, and the statistics at the end. It should make the parsing more robust and easier to maintain.
 
Last edited:
Upvote 0

Sergio Haurat

Active Member
Licensed User
Longtime User
Hello @JohnC, in the statistics line1 ChatGPT forgot the % of packets lost in the ping. Have you tried using it with the proposed modifications?
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
When I respond with a ChatGPT result, it is usually because I don't have the time to manually work on your issue, but I still want to help in some way.

So, if I believe that ChatGPT should be able to provide a solution to a particular issue, or at the very least, help move you closer to a solution, I will make a post. If not, then I won't post at all.
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
Hello @JohnC, in the statistics line1 ChatGPT forgot the % of packets lost in the ping. Have you tried using it with the proposed modifications?
I provided your comment to ChatGPT and I updated the code I posted with the newer version of it's code.
 
Last edited:
Upvote 0

Sergio Haurat

Active Member
Licensed User
Longtime User
I hope it's understood that it was just an inquiry if you had tried it. Thanks for responding. Now I'll try it myself (I also use ChatGPT) with the missing percentage and I'll comment on the result. I apologize if my response wasn't understood correctly; I use a translator to write my posts in English.
 
Upvote 0

JohnC

Expert
Licensed User
Longtime User
Because ChatGPT prompts don't allow formatting text in color, I used a trick to submit your issue to ChatGPT because your post included text in Red...

I took a screenshot of the paragraph containing certain text in red color and uploaded it to ChatGPT, which allowed it to then understand which text was in "Red", etc.

P.S. Thank you for clarifying the context of your reply.
 
Upvote 0

Sergio Haurat

Active Member
Licensed User
Longtime User
P.S. Thank you for clarifying the context of your reply.
No problem, Google, Microsoft and even ChatGPT translate from spanish text seem to have bad manners or forms. I have to learn to speak English well, not just understand it by reading it or listening when someone speak

New version with RegEx. There is something wrong with regexICMPSeq at line 36.
Line to find:
64 bytes from eze06s12-in-f4.1e100.net (142.250.79.100): icmp_seq=1 ttl=116 time=19.0 ms

matchICMPSeq.Find = false ????:
Public Sub PingRegEx(URL As String, Attempts As Int, Timeout As Int) As tpePing
    Dim pingResult As tpePing
    pingResult.Initialize
    If Attempts = 0 Then
        pingResult.Msg = "Attempts must be greater than or equal to 1"
    Else
        Dim sb As StringBuilder
        Dim p As Phone
        sb.Initialize
        p.Shell("ping -c" & Attempts & " -W" & Timeout & " -v " & URL, Null, sb, Null)
        If sb.Length = 0 Then
            pingResult.Msg = "Host unreachable"
        Else
            pingResult.Success = True
            pingResult.Msg = sb.ToString
            Dim aryResult() As String = Regex.Split(CRLF, pingResult.Msg)
            Log(sb.ToString)
            Dim blnIsFirstLine As Boolean = False
            Dim blnStats As Boolean = False
            Dim bteLine As Byte = 0
            Dim icmp_seq As Map
            icmp_seq.Initialize
            Dim matchStats As Matcher
            For Each lneResult As String In aryResult
                If Not (blnIsFirstLine) Then
                    Dim regexHostInfo As String = "PING ([^ ]+) \(([^)]+)\) "
                    blnIsFirstLine = True
                    ' Match host info
                    Dim matchHost As Matcher = Regex.Matcher(regexHostInfo, lneResult)
                    If matchHost.Find Then
                        pingResult.toHost = matchHost.Group(1)
                        pingResult.toIP = matchHost.Group(2)
                    End If
                Else If Not (blnStats)  Then
                    If lneResult <> "" And lneResult.IndexOf("ping statistics") = -1 Then
                        Dim regexICMPSeq As String = "from ([^ ]+) .* icmp_seq=([0-9]+) ttl=([0-9]+) .* time=([0-9.]+) ms"
                        Dim matchICMPSeq As Matcher = Regex.Matcher(regexICMPSeq, lneResult)
                        Dim mapSeq As Map
                        mapSeq.Initialize
                        If matchICMPSeq.Find Then
                            mapSeq.Put("from", matchICMPSeq.Group(1))
                            mapSeq.Put("ttl", matchICMPSeq.Group(3))
                            mapSeq.Put("time", matchICMPSeq.Group(4))
                            icmp_seq.Put(matchICMPSeq.Group(2), mapSeq)
                        End If
                    End If
                Else If blnStats Then
                    Dim mapStats As Map
                    mapStats.Initialize
                    pingResult.mapStatistics.Initialize
                    If bteLine = 0 Then
                        Dim regexStatsLine1 As String = "([0-9]+) packets transmitted, ([0-9]+) received, ([0-9]+)% packet loss, time ([0-9]+)ms"
                        matchStats = Regex.Matcher(regexStatsLine1, lneResult)
                        If matchStats.Find Then
                            mapStats.Put("packets transmitted", matchStats.Group(1))
                            mapStats.Put("received", matchStats.Group(2))
                            mapStats.Put("packet loss",matchStats.Group(3))
                            mapStats.Put("time", matchStats.Group(4))
                            pingResult.mapStatistics.Put("StatsLine1", mapStats)
                        End If
                    Else If bteLine = 1 Then
                        Dim regexStatsLine2 As String = "([0-9]+)/([0-9]+)/([0-9]+)/([0-9.]+) ms"
                        Dim matchStats As Matcher = Regex.Matcher(regexStatsLine2, lneResult)
                        If matchStats.Find Then
                            mapStats.Put("packets transmitted", matchStats.Group(1))
                            mapStats.Put("received", matchStats.Group(2))
                            mapStats.Put("packet loss", matchStats.Group(3))
                            mapStats.Put("time", matchStats.Group(4))
                            pingResult.mapStatistics.Put("StatsLine2", mapStats)
                        End If
                    End If
                    bteLine = bteLine + 1
                End If
                If Not (blnStats) Then
                    'Skip --- DEST_HOST ping statistics --- string and add map with all ping's result to pingResult
                    If lneResult.IndexOf("ping statistics") > 0 Then
                        pingResult.mapResponse = icmp_seq
                        blnStats = True
                    End If
                End If
            Next
        End If
    End If
    Return pingResult
  End Sub
 
Upvote 0

Sergio Haurat

Active Member
Licensed User
Longtime User
Find the solution, this is the correct line.
Dim regexICMPSeq As String = ".* from ([^ ]+) \(([^)]+)\): icmp_seq=([0-9]+) ttl=([0-9]+) time=([0-9.]+) ms"

The final code is the following
B4X:
Public Sub PingRegEx(URL As String, Attempts As Int, Timeout As Int) As tpePing
    Dim pingResult As tpePing
    pingResult.Initialize
    If Attempts = 0 Then
        pingResult.Msg = "Attempts must be greater than or equal to 1"
    Else
        Dim sb As StringBuilder
        Dim p As Phone
        sb.Initialize
        p.Shell("ping -c" & Attempts & " -W" & Timeout & " -v " & URL, Null, sb, Null)
        If sb.Length = 0 Then
            pingResult.Msg = "Host unreachable"
        Else
            pingResult.Success = True
            pingResult.Msg = sb.ToString
            Dim aryResult() As String = Regex.Split(CRLF, pingResult.Msg)
            Log(sb.ToString)
            Dim blnIsFirstLine As Boolean = False
            Dim blnStats As Boolean = False
            Dim bteLine As Byte = 0
            Dim icmp_seq As Map
            icmp_seq.Initialize
            Dim matchStats As Matcher
            For Each lneResult As String In aryResult
                If Not (blnIsFirstLine) Then
                    Dim regexHostInfo As String = "PING ([^ ]+) \(([^)]+)\) "
                    blnIsFirstLine = True
                    ' Match host info
                    Dim matchHost As Matcher = Regex.Matcher(regexHostInfo, lneResult)
                    If matchHost.Find Then
                        pingResult.toHost = matchHost.Group(1)
                        pingResult.toIP = matchHost.Group(2)
                    End If
                Else If Not (blnStats)  Then
                    If lneResult <> "" And lneResult.IndexOf("ping statistics") = -1 Then
                        'Dim regexICMPSeq As String = "from ([^ ]+) .* icmp_seq=([0-9]+) ttl=([0-9]+) .* time=([0-9.]+) ms"
                        Dim regexICMPSeq As String = ".* from ([^ ]+) \(([^)]+)\): icmp_seq=([0-9]+) ttl=([0-9]+) time=([0-9.]+) ms"
                        Dim matchICMPSeq As Matcher = Regex.Matcher(regexICMPSeq, lneResult)
                        Dim mapSeq As Map
                        mapSeq.Initialize
                        If matchICMPSeq.Find Then
                            mapSeq.Put("from", matchICMPSeq.Group(1))
                            mapSeq.Put("ttl", matchICMPSeq.Group(3))
                            mapSeq.Put("time", matchICMPSeq.Group(4))
                            icmp_seq.Put(matchICMPSeq.Group(2), mapSeq)
                        End If
                    End If
                Else If blnStats Then
                    Dim mapStats As Map
                    mapStats.Initialize
                    pingResult.mapStatistics.Initialize
                    If bteLine = 0 Then
                        Dim regexStatsLine1 As String = "([0-9]+) packets transmitted, ([0-9]+) received, ([0-9]+)% packet loss, time ([0-9]+)ms"
                        matchStats = Regex.Matcher(regexStatsLine1, lneResult)
                        If matchStats.Find Then
                            mapStats.Put("packets transmitted", matchStats.Group(1))
                            mapStats.Put("received", matchStats.Group(2))
                            mapStats.Put("packet loss",matchStats.Group(3))
                            mapStats.Put("time", matchStats.Group(4))
                            pingResult.mapStatistics.Put("StatsLine1", mapStats)
                        End If
                    Else If bteLine = 1 Then
                        Dim regexStatsLine2 As String = "rtt min/avg/max/mdev = ([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms"
                        'Dim regexStatsLine2 As String = "([0-9]+)/([0-9]+)/([0-9]+)/([0-9.]+) ms"
                        Dim matchStats As Matcher = Regex.Matcher(regexStatsLine2, lneResult)
                        If matchStats.Find Then
                            mapStats.Put("packets transmitted", matchStats.Group(1))
                            mapStats.Put("received", matchStats.Group(2))
                            mapStats.Put("packet loss", matchStats.Group(3))
                            mapStats.Put("time", matchStats.Group(4))
                            pingResult.mapStatistics.Put("StatsLine2", mapStats)
                        End If
                    End If
                    bteLine = bteLine + 1
                End If
                If Not (blnStats) Then
                    'Skip --- DEST_HOST ping statistics --- string and add map with all ping's result to pingResult
                    If lneResult.IndexOf("ping statistics") > 0 Then
                        pingResult.mapResponse = icmp_seq
                        blnStats = True
                    End If
                End If
            Next
        End If
    End If
    Return pingResult
End Sub
 
Upvote 1
Solution
Top