Link to home
Start Free TrialLog in
Avatar of brandonb
brandonb

asked on

Reading characters in a string.

I have a function that takes a compressed string, uncompresses it, and returns the uncompressed string.  The compression is very simple.  When 3 or more consecutive spaces or zeroes are encountered, they are removed and replaced with a flag (ASCII 126 for zeroes and ASCII 127 for spaces) and the binary number of spaces or zeroes.  All of this works properly.

My problem is that when I create an EXE, the program takes 2 to 3 times longer to uncompress the string than just running the program through the VB IDE.  Both are running the exact same code.

Any ideas on what the problem could be?  I can post the code if it will help.

Avatar of jrspano
jrspano
Flag of United States of America image

post the code.
Avatar of exelrud
exelrud

Is this a function or do you create another process to handle the uncompression or do it on the idle time ?
Avatar of brandonb

ASKER

Here is the code.  One other thing to mention about the compression is that if there are ASCII characters > 123, the compression routine doubles them as a way to let the uncompression routine know that they are truly characters not flags.

This is a function that is called from a .BAS module.

Function Uncompress_String(TalkInputString As String)

    Dim Char As String
    Dim NextChar As String
    Dim Cntr As Integer

    Uncompress_String = ""
   
    For X = 1 To Len(TalkInputString)
        Char = Mid$(TalkInputString, X, 1)
        If Char > Chr$(123) Then
            NextChar = Mid$(TalkInputString, X + 1, 1)
        End If
       
        If Char > Chr$(123) And Char = NextChar Then
            Uncompress_String = Uncompress_String + Char
        Else
            If Char = Chr$(126) Or Char = Chr$(127) Then
                Cntr = Asc(NextChar)
                    If Char = Chr$(127) Then
                        Uncompress_String = Uncompress_String + String(Cntr, " ")
                    Else
                        Uncompress_String = Uncompress_String + String(Cntr, "0")
                    End If
                X = X + 1
            Else
                Uncompress_String = Uncompress_String + Char
            End If
        End If
       
    Next X
   
End Function
I do not like using X = X + 1 inside a for X
change the for X for a while just to be sure.
The compiler otimizations may conflict with what you're trying to do (skip a char)
Ooops.
Change the for into a While.
This kind of manipulation would be a lot faster in a byte array.
PaulHews,
I haven't ever used a byte array.  Could you give me an example?  Speed is of the utmost importance.  This is a piece of a communications package between a UNIX C program and a VB program.  Some of our sites use a 56K lease line.  We have experimented with gzip compression, but found this simple compression gives us the best combination of compression and processing time.  Anything that would speed up the compression/uncompression would be greatly appreciated.

exelrud,
I understand the concern with adding 1 to X, but would I not have the same problem running the program through the IDE?
ASKER CERTIFIED SOLUTION
Avatar of exelrud
exelrud

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
This is what I mean...  I can't really test this, so there may be bugs, but it is the broad idea of what I mean:

Function Uncompress_String(TalkInputString As String) As String

   Dim Char As Byte
   Dim NextChar As Byte
   Dim Cntr As Integer
   Dim bytInputString() As Byte
   Dim bytUncompressedString() As Byte  'Output string
   Dim intChunkSize As Integer  'used to allocate memory in 1K blocks (faster)
   Dim intCount As Integer  'incremented for each addition to output
   'AscII constants
   Const chSpace As Byte = 32
   Const chZero As Byte = 48
   
   intChunkSize = 1024
    bytInputString = StrConv(TalkInputString, vbFromUnicode)  'Convert string to bytes
   
'   Uncompress_String = ""
   intCount = 0
   
   For X = 1 To Len(TalkInputString)
       Char = bytInputString(i - 1) 'Mid$(TalkInputString, X, 1)
       If Char > 123 Then
           NextChar = bytInputString(i - 1) 'Mid$(TalkInputString, X + 1, 1)
       End If
       
       If Char > 123 And Char = NextChar Then
            'This code could go in a sub for readability, but a little faster inline.  :)
            intCount = intCount + 1
            If intCount > intChunkSize Then
                intChunkSize = intChunkSize + 1024
                ReDim Preserve bytUncompressedString(1 To intChunkSize)
            End If
            bytUncompressedString(intCount) = Char
           
           'Uncompress_String = Uncompress_String + Char
       Else
           If Char = 126 Or Char = 127 Then
               Cntr = NextChar
                    intCount = intCount + 1
                    If intCount > intChunkSize Then
                        intChunkSize = intChunkSize + 1024
                        ReDim Preserve bytUncompressedString(1 To intChunkSize)
                    End If
                   
                   If Char = 127 Then
                        bytUncompressedString(intCount) = chSpace
'                       Uncompress_String = Uncompress_String + String(Cntr, " ")
                   Else
                        bytUncompressedString(intCount) = chZero
'                       Uncompress_String = Uncompress_String + String(Cntr, "0")
                   End If
               X = X + 1
           Else
                intCount = intCount + 1
                If intCount > intChunkSize Then
                    intChunkSize = intChunkSize + 1024
                    ReDim Preserve bytUncompressedString(1 To intChunkSize)
                End If
                bytUncompressedString(intCount) = Char
'               Uncompress_String = Uncompress_String + Char
           End If
       End If
       
   Next X
   Uncompress_String = StrConv(bytUncompressedString, vbUnicode)   'Convert bytes back to string
   
End Function
Commented out your code where mine was different...

This should perform significantly faster, especially if the strings are long.
Found one bug already:

If Char > 123 Then
          NextChar = bytInputString(i) 'Mid$(TalkInputString, X + 1, 1)
      End If
I'm going to give both of you points for your help.  exelrud seems to have solved my problem and I'm just beginning to test PaulHews code.
Built for speed.. <smile>

=====================================================
1. Start a New Standard.Exe Project.
2. Copy/Paste the following into the Form1 code window.
3. Press F8 to Run. The contents of strWork will appear in a MsgBox.

<----- Code Begin ----->
Option Explicit

Private Sub Form_Load()
   
   Dim strWork As String
   strWork = "a" & Chr(127) & Chr(12) & "b"
   MsgBox Uncompress_String(strWork)

End Sub

Function Uncompress_String(TalkInputString As String)

   Dim bytGet() As Byte: ReDim bytGet(Len(TalkInputString) * 1)
   Dim lngGet As Long: lngGet = 0
   Dim bytPut() As Byte: ReDim bytPut(Len(TalkInputString) * 2) ' Estimate
   Dim lngPut As Long: lngPut = 0
   Dim lngX As Long
   
   bytGet() = StrConv(TalkInputString & ":", vbFromUnicode)

   For lngGet = LBound(bytGet) To UBound(bytGet) - 1
      If (bytGet(lngGet) = 126 And bytGet(lngGet + 1) <> 126) _
      Or (bytGet(lngGet) = 127 And bytGet(lngGet + 1) <> 127) _
      Then
         For lngX = 1 To bytGet(lngGet + 1)
            Select Case bytGet(lngGet)
               Case 126
                  bytPut(lngPut) = Asc(" ")
               Case 127
                  bytPut(lngPut) = Asc("0")
            End Select
            lngPut = lngPut + 1
            If lngPut >= UBound(bytPut) Then
               ReDim Preserve bytPut(lngPut)
            End If
         Next lngX
         lngGet = lngGet + 1
      Else
         If bytGet(lngGet) > 123 And bytGet(lngGet) = bytGet(lngGet + 1) Then
            lngGet = lngGet + 1
         End If
         bytPut(lngPut) = bytGet(lngGet)
         lngPut = lngPut + 1
         If lngPut >= UBound(bytPut) Then
            ReDim Preserve bytPut(lngPut)
         End If
      End If
   Next lngGet
   
   ReDim Preserve bytPut(lngPut - 1)
   Uncompress_String = StrConv(bytPut(), vbUnicode)
   
End Function

<----- Code End ----->
wsh2,
Your code works great except if I change anything after pasting the code in my program.  If I add some blank lines or realign the beginning of each line with the program, I receive the following error when I try to run the program.  Sometimes, when VB restarts, the error doesn't occur again, but other times it still does.  Any ideas?

The instruction at "0x0facb9ac" referenced memory at "0x00000000". The memory could not be "read".

Click on OK to terminate the program
Click on CANCEL to debug the program
brandonb..

It sounds like your copy of Visual Basic is corrupt.. <sigh>. You may want to consider reinstalling it and applying VB6 SP5 which you can get at this URL address:

http://msdn.microsoft.com/vstudio/sp/vs6sp5
Thanks for help everyone.
Ah.  Did you intentionally choose that answer, or was it a mistake.
Intentionally.  exelrud's suggestion solved my original problem.

Check the question list.  There should be a "question" just for you for your help with the byte array.

BTW, the error I was receiving has something to do with the code not VB being corrupt.  I haven't had the time to narrow it down, but I modified PaulHews code and it works wonderfully.