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
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Upvote 0

Wosl

Member
Hi Erel, I'm very much pleased to get comments from you!

To #1a:
The StringUtils.SaveCSV is a great function to export into CSV type file (in combination with StringUtils.SaveCSV2 for the header) . What you need is the table structure as described in the documentation: A List with arrays of strings as items. Each array represents a row. All arrays should be of the same length. This would require for me to build up this table structure from my internal data structure 'PowerStrucX' to export the whole set of data into an external file on my SD card. This sounds to be not a big deal and would avoid the usage of StringBuilder. I'll will give this approach a try.

To #1b:
I need to check your link to the CSV parser. However, generating a compliant CSV file is not the issue in my case (I beleave).

To #2:
B4X:
lLength = lLength + sb.Length
Yes, you are right. 'lLength' is not neccessary. I'll change the coding accordingly.
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
For a start, I'd change the 17 string variables c1..c17 into a string array Dim c(17) As String.

Then your line constructor becomes:

B4X:
For Each ci As String In c
    sb.Append(ci).Append(";")
Next
sb.Append(CRLF)

or if you don't want the trailing semicolon, maybe:

B4X:
For I = 0 To c.Length - 1
    If I > 0 Then sb.Append(";")
    sb.Append(c(I))
Next
sb.Append(CRLF)

and then personally I'd just add each (every) line to your external file, one by one, as they come, eg:

B4X:
'  Append to external file
lTotalLength = lTotalLength + sb.Length
AppendExternalStorage (sb.ToString)
sb.Initialize

but if you want to write them out in bursts, then it'd be:

B4X:
Dim nStringBuffer As Int = 100000    'couldn't find this value (setting, parameter) in your example
If sb.Length > nStringBuffer Or i = nValues Then    'last line is flushed here, no junk left to write to file
    '  Append to external file
    lTotalLength = lTotalLength + sb.Length
    AppendExternalStorage (sb.Append(CRLF)ToString)
    sb.Initialize
End If
 
Last edited:
Upvote 0

emexes

Expert
Licensed User
Longtime User
I am assuming that iJunk is really iChunk, and that it is just something you've added for debugging.

I too am mystified as to why the last column of the previous line would be appearing at the start of the next line. Are you sure about that?
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
Another minor simplification... instead of:

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

consider:

B4X:
For i = 1 To nPowerTableName
    If i > 1 Then sb.Append(";")
    sb.Append(PowerTableName(i - 1) & cExt)
Next

so that if the construction of the column name ever becomes more complicated, you only have to make the change once, not twice (= one less thing to forget to do).
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
mystified as to why the last column of the previous line would be appearing at the start of the next line

What's also interesting is that the decimal point in the "first" column is still a point, but in the last column it is a comma.

Add this debug line, maybe that'll give us a clue:

B4X:
For i = 1 To nValues
    '  Setup row of data
    c1  = PowerStrucX.TimestampList.Get(i-1)
    Log(i & Tab & c1.Length & Tab & c1)
    c2  = PowerStrucX.DatumList.Get(i-1)
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
the decimal point in the "first" column is still a point

I'd be having a close look at the code that constructs the timestamps and adds them to PowerStrucX.TimestampList

Also, what time zone are you in? Not that it matters that much: I usually check the forum anyway if I wake up in the middle of the night here. 🍻
 
Upvote 0

Wosl

Member
Hi emexes, I appreciate very much that you spend your time on my issue.

For sure I'll incorporate your proposals, test it and post the results. But it will take some time.

However, just let me comment shortly your proposals.

To post #4 and #7
To put the columns c1 to c17 into an array simplifies the append loop. Even the ';' can be included. But this line I don't understand:
B4X:
AppendExternalStorage (sb.Append(CRLF)ToString)
It should be, or?
B4X:
AppendExternalStorage (sb.ToString)
The String Buffet is set in Globals to 4000000, but the value doesn't matter here.

To post #5
The variable iJunk is just a counter for the amount of I/O Operations (append to external file).
The magic occurence of the last column in the first column of the next row is validated in log file and in the external file. But I can check c1 to validate it, too

To post #6
I agree

To post #7
The timestamp is stored in c1 and is correct. For sure i consider the time zone.

Let me modify my code and test it. I will provide a feedback as soon as passable.
 
Upvote 0

Wosl

Member
I modified the code as proposed:
- Header row as proposed in post #6
- Data rows as proposed in post #4 (introducing an array for columns)

The sub looks like this now:
B4X:
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
    Private sb As StringBuilder, iJunk As Int
     Private c(17) 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(";")
        sb.Append(PowerTableName(i - 1) & cExt)
    Next
    sb.Append(CRLF)
    lTotalLength = sb.Length

    '---------------------------------------------------------------
    ' 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
        c(0)  = PowerStrucX.TimestampList.Get(i-1)
        c(1)  = PowerStrucX.DatumList.Get(i-1)
        c(2)  = ((RFormat(PowerStrucX.UhrzeitList.Get(i-1), 6, 14, True)).Trim).Replace(".",",")
        c(3)  = PowerStrucX.UhrzeitListText.Get(i-1)
        c(4)  = ((RFormat(PowerStrucX.UhrzeitListX.Get(i-1), 6, 14, True)).Trim).Replace(".",",")
        c(5)  = RFormat(PowerStrucX.ProduktionList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(6)  = RFormat(PowerStrucX.VerbrauchList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(7)  = RFormat(PowerStrucX.DirektverbrauchList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(8)  = RFormat(PowerStrucX.BatterieentladungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(9)  = RFormat(PowerStrucX.NetzbezugList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(10) = RFormat(PowerStrucX.NetzeinspeisungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(11) = RFormat(PowerStrucX.BatterieladungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(12) = RFormat(PowerStrucX.SpeicherladungList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(13) = RFormat(PowerStrucX.WorkTodayList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(14) = RFormat(PowerStrucX.WorkMonthList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(15) = RFormat(PowerStrucX.WorkYearList.Get(i-1), iFraction, 8, True).Replace(".",",")
        c(16) = RFormat(PowerStrucX.WorkTotalList.Get(i-1), iFraction, 8, True).Replace(".",",")

        For Each ci As String In c
            sb.Append(ci).Append(";")
        Next
        sb.Append(CRLF)
        lTotalLength = lTotalLength + sb.Length

        'WOSL###########################
        '  log first 5 rows
        If i < 6 Then
            Log (" Row " & i & ":")
            Log ("  c(0), c(16) = " & c(0) & ", " & c(16))
            Log ("  sb length = " & sb.Length & "; sb: " & sb.ToString)
            Log (" ")
        End If
         'WOSL###########################

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

    Next

    '  Clear string buffer
    If sb.Length > 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

The log is looking like this:
B4X:
Number of rows processed:    736
 Row 1:
  c(0), c(16) = T20250911000033,  17,9971
  sb length = 171; sb:  17.9971T20250911000033;11.09.2025;0,009167;00:00:33;0,009167;  0,0000;  0,2836;  0,0000;  0,0000;  0,2836;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 2:
  c(0), c(16) = T20250911000135,  17,9971
  sb length = 171; sb:  17.9971T20250911000135;11.09.2025;0,026389;00:01:35;0,026389;  0,0000;  0,2848;  0,0000;  0,0000;  0,2848;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 3:
  c(0), c(16) = T20250911000237,  17,9971
  sb length = 171; sb:  17.9971T20250911000237;11.09.2025;0,043611;00:02:37;0,043611;  0,0000;  0,2875;  0,0000;  0,0000;  0,2875;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 4:
  c(0), c(16) = T20250911000338,  17,9971
  sb length = 171; sb:  17.9971T20250911000338;11.09.2025;0,060556;00:03:38;0,060556;  0,0000;  0,2898;  0,0000;  0,0000;  0,2898;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 5:
  c(0), c(16) = T20250911000440,  17,9971
  sb length = 171; sb:  17.9971T20250911000440;11.09.2025;0,077778;00:04:40;0,077778;  0,0000;  0,2844;  0,0000;  0,0000;  0,2844;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
CSV File 'PowerDayService_kW.csv' with power data (Day) successfully written to 'PVPerformanceData' with 126474 Bytes in 1 junks.

and the external file content is as following:
B4X:
Timestamp_kW;Datum_kW;Uhrzeit_kW;UhrzeitText_kW;UhrzeitX_kW;ProduktionPower_kW;VerbrauchPower_kW;DirektverbrauchPower_kW;BatterieentladungPower_kW;NetzbezugPower_kW;NetzeinspeisungPower_kW;BatterieladungPower_kW;SpeicherladungPower_kW;WorkTodayPower_kW;WorkMonthPower_kW;WorkYearPower_kW;WorkTotalPower_kW
 18.0017T20250911124052;11.09.2025;12,681111;12:40:52;12,681111;  2,4734;  2,4992;  2,4734;  0,0000;  0,0258;  0,0000;  0,0000;  0,0000;  0,0000;  0,2588;  0,6061; 18,0017;

Analysis:
- Setting the individual columns like c(0), c(1) ... is as expected
- Storing the data columns into the StringBuilder 'sb' failed.
The log shows that the variable 'sb' doesn't increment in length for each new row. It stays constantly 171 characters long.
The string 'sb' is wrong because it starts always with the last column of the previous row (here: 18.001, surprisingly with decimal point instead of comma)
- The export file is wrong, too
Header row is ok
Only the last row (here data point 736) is stored but with the misspelled string 'sb'

Conclusion:
The annoying thing is that I'm doing something wrong but can not find the mistake. Or the Stringbuilder module is doing something strange in my case.

The next step will be that I'm looking for alternatives, like Erel's proposal.

Thank you Erel & emexes for your support.

Wosl
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
The next step will be that I'm looking for alternatives

Hang on, we're not done here yet 🍻 what you showed us, should work 🍻🍻 did you try post #7 ie:

Add this debug line, maybe that'll give us a clue:

B4X:
For i = 1 To nValues
    '  Setup row of data
    c(0)  = PowerStrucX.TimestampList.Get(i-1)
    Log(i & Tab & c(0).Length & Tab & c(0))
    c(1)  = PowerStrucX.DatumList.Get(i-1)

Never mind, I just spotted that you've effectively done this already, for the first 5 rows. ☮️
 
Last edited:
Upvote 0

emexes

Expert
Licensed User
Longtime User
Or the Stringbuilder module is doing something strange in my case.

No, it should work. The only thing in the code you've shown us that is remotely dodgy, is using .Initialize multiple times on the one object, but I am sure I've done that too with StringBuilder, and Grok agrees with us that it should work:


and so I am super-keen to get to the bottom of this mystery.
 
Last edited:
Upvote 0

emexes

Expert
Licensed User
Longtime User
Add these 3 debug lines:

B4X:
c(16) = RFormat(PowerStrucX.WorkTotalList.Get(i-1), iFraction, 8, True).Replace(".",",")

Dim LengthBefore As Int = sb.Length
For Each ci As String In c
    sb.Append(ci).Append(";")
Next
sb.Append(CRLF)
Dim LengthAfter As Int = sb.Length
lTotalLength = lTotalLength + sb.Length

'WOSL###########################
'  log first 5 rows
If i < 6 Then
    Log (" Row " & i & ":")
    Log ("  c(0), c(16) = " & c(0) & ", " & c(16))
    Log ("  sb length = " & sb.Length & "; sb: " & sb.ToString)
    Log ("  LengthBefore = " & LengthBefore & "; LengthAfter = " & LengthAfter & "; nStringBuffer = " & nStringBuffer)
    Log (" ")
End If
'WOSL###########################
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
I am wondering if the CRLF at the end of the line (which is really just a LF ie ASCII 0x0A) is causing problems for Log()

Although I've just tried it here with B4J and it works just fine.

(don't have working B4A installed)
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
nevergiveup.jpg
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
Hi emexes, I appreciate very much that you spend your time on my issue.

Hey, it's an interesting and entertaining problem, so it's me that should be thanking you for coming up with the puzzle. 🍻

But this line I don't understand:
B4X:
AppendExternalStorage (sb.Append(CRLF)ToString)
It should be, or?
B4X:
AppendExternalStorage (sb.ToString)

My mistake. Every now and then when I do a Ctrl+V paste, nothing visibly happens, and I wonder: where the heck did that text end up?

I think what happens is that sometimes a finger hovers too close to the mousepad and moves the cursor without me knowing.
 
Upvote 0

Wosl

Member
So, believe it or not ... I found an alternative which works.

After a research on StringBuilder posts in our community I found a library of an extended StringBuilder version posted by carlos7000 in Jun 22, 2024:

Only a few adaptations are required to my code in post #11:
Introduction of extended Stringbuilder (except for the header) with
B4X:
Private sbEnh As EnhancedStringBuilder
Some changes regarding the '.length' method which is unknown in EnhancedStringbuilder. So a change to all statements containing 'length' to
B4X:
sbEnh.GetLength
and lastly split the conncatition of ".append" in single statements
B4X:
        For Each ci As String In c
            sbEnh.Append(ci)
            sbEnh.Append(";")
        Next

With the mentioned modifications the new log is as following:
B4X:
Number of rows processed:    921
 Row 1:
  c(0), c(16)  = T20250911000033,  17,9971
  sbEnh length = 163; sbEnh: T20250911000033;11.09.2025;0,009167;00:00:33;0,009167;  0,0000;  0,2836;  0,0000;  0,0000;  0,2836;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 2:
  c(0), c(16)  = T20250911000135,  17,9971
  sbEnh length = 326; sbEnh: T20250911000033;11.09.2025;0,009167;00:00:33;0,009167;  0,0000;  0,2836;  0,0000;  0,0000;  0,2836;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000135;11.09.2025;0,026389;00:01:35;0,026389;  0,0000;  0,2848;  0,0000;  0,0000;  0,2848;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 3:
  c(0), c(16)  = T20250911000237,  17,9971
  sbEnh length = 489; sbEnh: T20250911000033;11.09.2025;0,009167;00:00:33;0,009167;  0,0000;  0,2836;  0,0000;  0,0000;  0,2836;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000135;11.09.2025;0,026389;00:01:35;0,026389;  0,0000;  0,2848;  0,0000;  0,0000;  0,2848;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000237;11.09.2025;0,043611;00:02:37;0,043611;  0,0000;  0,2875;  0,0000;  0,0000;  0,2875;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 4:
  c(0), c(16)  = T20250911000338,  17,9971
  sbEnh length = 652; sbEnh: T20250911000033;11.09.2025;0,009167;00:00:33;0,009167;  0,0000;  0,2836;  0,0000;  0,0000;  0,2836;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000135;11.09.2025;0,026389;00:01:35;0,026389;  0,0000;  0,2848;  0,0000;  0,0000;  0,2848;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000237;11.09.2025;0,043611;00:02:37;0,043611;  0,0000;  0,2875;  0,0000;  0,0000;  0,2875;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000338;11.09.2025;0,060556;00:03:38;0,060556;  0,0000;  0,2898;  0,0000;  0,0000;  0,2898;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
 Row 5:
  c(0), c(16)  = T20250911000440,  17,9971
  sbEnh length = 815; sbEnh: T20250911000033;11.09.2025;0,009167;00:00:33;0,009167;  0,0000;  0,2836;  0,0000;  0,0000;  0,2836;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000135;11.09.2025;0,026389;00:01:35;0,026389;  0,0000;  0,2848;  0,0000;  0,0000;  0,2848;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000237;11.09.2025;0,043611;00:02:37;0,043611;  0,0000;  0,2875;  0,0000;  0,0000;  0,2875;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000338;11.09.2025;0,060556;00:03:38;0,060556;  0,0000;  0,2898;  0,0000;  0,0000;  0,2898;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
T20250911000440;11.09.2025;0,077778;00:04:40;0,077778;  0,0000;  0,2844;  0,0000;  0,0000;  0,2844;  0,0000;  0,0000;  0,0000;  0,0000;  0,2542;  0,6014; 17,9971;
 
CSV File 'PowerDayService_kW.csv' with power data (Day) successfully written to 'PVPerformanceData' with 69323631 Bytes in 1 junks.

Everything looks fine now, the exported file content, too.

My conclusion:
The core StringBuilder seems to have an issue, at least with my code, which could be still wrong. I'm unable to detect the difference between core StringBuilder and ExtendedStringBuilder but may be somebody having access to both program codes can do a comparison. Obviously, I'm keen to understand the issue.

However, emexes I appreciate very much your fast and excellent support on my issue.

Wosl
 
Upvote 0

emexes

Expert
Licensed User
Longtime User
I'm keen to understand the issue.

How keen?

Because what we've done so far is cast doubt on the built-in StringBuilder that is probably undeserved, and certainly mysterious, and if there is indeed a problem with it then we should find it whilst it is visible, so that it can be fixed.

On the other hand: time is money.

My first thought is that you shouldn't have had to change the name of sb to sbEnh. Perhaps that was what fixed the problem. Change the name back to what it was, ie change sbEnh back to sb. Preferably manually, rather than the IDE search-and-replace function that could change unknown extras that might be left over from debugging.

Lol I'm a bit sad that @carlos7000 didn't make his enhancement drop-in compatible. But what's done is done - too late to fix it now.

Anyway, if that still works, and it doesn't unmask any unknown extra usage of sb, then the next easy test would be to duplicate ("shadow") each of the EnhancedStringBuilder Dim, .Initialize and .Append lines with a corresponding regular StringBuilder line, eg:

B4X:
Private sb As EnhancedStringBuilder
Private testsb As StringBuilder
B4X:
For Each ci As String In c
    sb.Append(ci)
    testsb.Append(ci)

    sb.Append(";")
    testsb.Append(";")
Next

and then compare sb.ToString with testsb.ToString eg:

B4X:
'  Clear string buffer
    If lLength > 0 Then
        If testsb.ToString <> sb.ToString Then
            Log("wtf StringBuilder ?!")
            Log(sb.ToString)
            Log(testsb.ToString)
        End If
        AppendExternalStorage (sb.ToString)
        iJunk = iJunk + 1
    End If
    sb.Initialize
 
Upvote 0
Top