Android Question Strange behaviour of StringBuilder

Wosl

Member
I need some guidance on StringBuilder module. This function seems to be as simple as possible, but ...

The goal is to export data from internal structure 'PowerStrucX' to an external file in a CSV format to be able to import the data into MS Excel.

To get an appropriate Excel format I transform for each row each column with several operations (e.g. constant float point length, change decimal point to comma) into string variables (c1 to c17). This is all fine and works as expected. Because the data could be large I combine the each row and column with StringBuilder to one variable 'sb'. When 'sb' length exceeds a defined bufferlength, the variable is exported by appending to an existing file. The export itself works as expected (file management etc.) but the content is very strange.

StringBuilder:
Sub OutputCSVPower (cFileDir As String, cFileCSV As String, cExt As String, _
                    cDataType As String, PowerStrucX As PowerStruc) As ResumableSub
    Private nValues As Long, iFraction As Int, cReturn As String
    Private lTotalLength As Long, lLength As Long
    Private sb As StringBuilder, iJunk As Int
    Private c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14, c15, c16, c17 As String

    '---------------------------------------------------------------
    '  Generate CSV Header and fraction of floating point numbers
    '---------------------------------------------------------------
    sb.Initialize
    If iPowerUnitIndicator = 0 Then
        iFraction = 1
    Else If iPowerUnitIndicator = 1 Then
        iFraction = 4
    Else If iPowerUnitIndicator = 2 Then
        iFraction = 7
    End If
    For i = 1 To nPowerTableName
        If i = 1 Then
            sb.Append(PowerTableName(i-1) & cExt)
        Else
            sb.Append(";" & PowerTableName(i-1) & cExt)
        End If
    Next
    sb.Append(CRLF)
    lTotalLength = sb.Length
    lLength      = lTotalLength

    '---------------------------------------------------------------
    ' Output header to new CSV file
    '---------------------------------------------------------------   
    Wait For (OpenOutputExternalStorage (cPVDataStorageFolder, cFileCSV, sb, False)) complete (oRes As Object)

    '---------------------------------------------------------------
    '  Loop through data rows
    '---------------------------------------------------------------
    nValues = PowerStrucX.TimestampList.Size
    If nValues < 1 Then
        Return "3"
    End If

    sb.Initialize
    iJunk = 0
    For i = 1 To nValues
        '  Setup row of data
        c1  = PowerStrucX.TimestampList.Get(i-1)
        c2  = PowerStrucX.DatumList.Get(i-1)
        c3  = ((RFormat(PowerStrucX.UhrzeitList.Get(i-1), 6, 14, True)).Trim).Replace(".",",")
        c4  = PowerStrucX.UhrzeitListText.Get(i-1)
        c5  = ((RFormat(PowerStrucX.UhrzeitListX.Get(i-1), 6, 14, True)).Trim).Replace(".",",")
        c6  = RFormat(PowerStrucX.ProduktionList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c7  = RFormat(PowerStrucX.VerbrauchList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c8  = RFormat(PowerStrucX.DirektverbrauchList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c9  = RFormat(PowerStrucX.BatterieentladungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c10 = RFormat(PowerStrucX.NetzbezugList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c11 = RFormat(PowerStrucX.NetzeinspeisungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c12 = RFormat(PowerStrucX.BatterieladungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c13 = RFormat(PowerStrucX.SpeicherladungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c14 = RFormat(PowerStrucX.WorkTodayList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c15 = RFormat(PowerStrucX.WorkMonthList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c16 = RFormat(PowerStrucX.WorkYearList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c17 = RFormat(PowerStrucX.WorkTotalList.Get(i-1), iFraction, 8, True).Replace(".",",")

        sb.Append(c1).Append(";").Append(c2).Append(";").Append(c3).Append(";").Append(c4).Append(";").Append(c5).Append(";"). _
           Append(c6).Append(";").Append(c7).Append(";").Append(c8).Append(";").Append(c9).Append(";").Append(c10).Append(";"). _
           Append(c11).Append(";").Append(c12).Append(";").Append(c13).Append(";").Append(c14).Append(";").Append(c15).Append(";"). _
           Append(c16).Append(";").Append(c17).Append(";").Append(CRLF)
        'WOSL###########################
        Log ("Länge: " & sb.Length & " sb: " & sb.ToString)
        'WOSL###########################

        '  Append to external file
        lTotalLength = lTotalLength + sb.Length
        lLength = lLength + sb.Length
        If lLength > nStringBuffer Then
            AppendExternalStorage (sb.ToString)
            iJunk = iJunk + 1
            sb.Initialize
            lLength = 0
        End If

    Next

    '  Clear string buffer
    If lLength > 0 Then
        AppendExternalStorage (sb.ToString)
        iJunk = iJunk + 1
    End If
    sb.Initialize

    Log ("CSV File '" & cFileCSV & "' with power data (" & cDataType & ") successfully written to '" & _
          cFileDir & "' with " & lTotalLength & " Bytes in " & iJunk & " junks.")

    cReturn = "0"
    Return cReturn
End Sub

The log file looks like this ...
Log:
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create (first time) **
Can use persistant uri!
** Activity (main) Resume **
Get PV power data from SQLite DB on NAS, calculate work data and plot results
 
Generate 'Day' Chart (daily power flow chart in main activity)
TimestampFromToday, TimestampToToday, DateTime: T20250909000000, T20250909235959 (09.09.2025 00:00:00, 09.09.2025 23:59:59)
------------------------------------------------------------------
Get power data from webserver
Selected timeframe: T20250909000000 to T20250909235959
*** Receiver (httputils2service) Receive (first time) ***
Number of rows processed:    941
Länge: 171 sb:  17.9761T20250909000027;09.09.2025;0,007500;00:00:27;0,007500;  0,0000;  0,3138;  0,0000;  0,0000;  0,3138;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9761;
Länge: 171 sb:  17.9761T20250909000129;09.09.2025;0,024722;00:01:29;0,024722;  0,0000;  0,3120;  0,0000;  0,0000;  0,3120;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9761;
Länge: 171 sb:  17.9761T20250909000231;09.09.2025;0,041944;00:02:31;0,041944;  0,0000;  0,3106;  0,0000;  0,0000;  0,3106;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9761;
Länge: 171 sb:  17.9761T20250909000333;09.09.2025;0,059167;00:03:33;0,059167;  0,0000;  0,3408;  0,0000;  0,0000;  0,3408;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9761;
Länge: 171 sb:  17.9761T20250909000435;09.09.2025;0,076389;00:04:35;0,076389;  0,0000;  0,3111;  0,0000;  0,0000;  0,3111;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9761;
Länge: 171 sb:  17.9761T20250909000537;09.09.2025;0,093611;00:05:37;0,093611;  0,0000;  0,3394;  0,0000;  0,0000;  0,3394;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9761;
....

Depending on the selected string buffer length (i played around with this buffer) the file content shows all rows or only some.

However, two things are strange:
1. Each row starts with the content of the last string c17 ('17,9761'). It should start with a time stamp 'T20250909....'. When I delete c17 (use only c1 to c16) the string starts with the content of c16 ('0,6014')
2. The content of the file is incomplete (some times), which has nothing to do with the file operations but with the string buffer. I can skip the export but #1 is still wrong.

It seems that my implementation and usage of the StringBuilder variable 'sb' is wrong but I don't see the reason why.

It is nearly impossible to create a simple program to simulate the issue. I tried this (same sub, but some simulated data structure) and what happens: it worked!!

Any hint to overcome this issue is appreciated.

Wosl
 

emexes

Expert
Licensed User
Longtime User
My first thought is that you shouldn't have had to change the name of sb to sbEnh.

Your english is way better than my german, but just to head off a possible lost-in-translation issue that might otherwise cause insult:

I'm not saying you shouldn't have changed it. I'm saying that you shouldn't have needed to change it, and the fact that you put in the extra effort to change it when you didn't need to, makes me think that maybe there was an error or warning message when you first tried it without changing the name, that might help us discover what the heck is going on. 🍻
 
Upvote 0
Top