Link to home
Start Free TrialLog in
Avatar of bsavas
bsavas

asked on

How to create an array from a string using MoveMemory function.

Hi,
I have a very big text containing over 30000 characters. And i want to know how to create an array from that string. It's not reasonable to use Mid$() function due to low speed.

for example;
Dim strTxt   as string
dim strArr() as string

strTxt = Text1.Text
....
How to make an array from this text..
I want to analyse each of the characters. (Japanese or Roman)
Avatar of rdrunner
rdrunner

What do you want in each array element ?

One or two bytes?
you could use Split() function if there are certain characters you can seperate the string by (ie ;)

if not then you probably dont have much choice using something like

=======
dim myArray(2000) as string

intStringLen = len(strMainString)  'strMainString being your 300000 char string

intChunkSize = intStringLen / 2000  'calculate the size of each string piece to add into each array cell

for i = 0 to intChunkSize - 1
     myArray(i) = left(strMainString, intChunkSize)
     tempStr = right(strMainString, len(strMainString) - intChunkSize 'Truncate the main string each time
next
=======

probably be v slow though!
there should be another line before the 'next' statement:

strMainString = tempStr
Ok it works now ;)

here is the code :

Private Sub Command4_Click()
Dim ar1() As dblByte
Dim cLine As String
cLine = "Test for strings in VB"
ReDim ar1(Len(cLine))
CopyMemory ByVal VarPtr(ar1(0)), ByVal StrPtr(cLine), Len(cLine) * 2
DoEvents
End Sub
'definitions:
Public Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Public Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As Long)
Public Type dblByte
    Hi As Byte
    Lo As Byte
End Type
P.s: It also works if you use a Integer as datatype (For the array ar1)..

To get the character for each letter back you just do

Debug.Print Chr$(ar1(0))

You could use a dynamic byte array.

Example:
Dim b() as byte
Dim s as string

s="abc...30000 chars"
b=s

b(0) will be 97 (ascii 'a')
b(1) will be 0
b(2) will be 98 (ascii 'b')
b(3) will be 0

etc.

You will get 2 bytes per char in string in unicode systems, hence the 0 after each char.

Hence to access the nth char, you use b(((n-1) * 2))

This is also very fast:

Option Explicit

Private Sub Command1_Click()
    Dim strText As String
    Dim bytArr() As Byte
    Dim strArr() As String
    Dim i As Long
    Dim sngTime As Single
   
    sngTime = Timer
   
    strText = String(30000, "X")
    bytArr = StrConv(strText, vbFromUnicode)
    ReDim strArr(0 To UBound(bytArr))
    For i = 0 To UBound(bytArr)
        strArr(i) = Chr(bytArr(i))
    Next i
    MsgBox Timer - sngTime & " s"
   
End Sub
About very Fast ... ;)

I measured your way vs my way with a slightly bigger string (300K)

Result :
Your way       My way    
 0,28125       0

I made the string bigger since at 30 K i got 0 for my Version... At 300K it still returns 0 *evilgrin*

'Snipp to test

Private Sub Form_Load()
   
   Dim cLine As String
   Dim bytArr() As Byte
   Dim strArr() As String
   Dim i As Long
   Dim sngTime As Single
   Dim nTotal1 As Single
   Dim nTotal2 As Single
   Dim ar1() As Integer
   sngTime = Timer
   
   cLine = String(300000, "X")
   bytArr = StrConv(cLine, vbFromUnicode)
   ReDim strArr(0 To UBound(bytArr))
   For i = 0 To UBound(bytArr)
       strArr(i) = Chr(bytArr(i))
   Next i
   nTotal1 = Timer - sngTime
   sngTime = Timer
   
   ReDim ar1(Len(cLine))
   CopyMemory ByVal VarPtr(ar1(0)), ByVal StrPtr(cLine), Len(cLine) * 2
   
   nTotal2 = Timer - sngTime
   
   Debug.Print nTotal1, nTotal2
   
End Sub
Right and where do you produce an array of strings, as asked in the question, using your method?  If I leave it as a byte array there is only a small difference between your method and mine at len = 300000, which is about 3x larger than the original question asked for.

Option Explicit
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As Long)

Private Sub Form_Load()
 
  Dim cLine As String
  Dim bytArr() As Byte
  Dim strArr() As String
  Dim i As Long
  Dim sngTime As Single
  Dim nTotal1 As Single
  Dim nTotal2 As Single
  Dim ar1() As Integer
  sngTime = Timer
 
  cLine = String(300000, "X")
  bytArr = StrConv(cLine, vbFromUnicode)
  ReDim strArr(0 To UBound(bytArr))
'  For i = 0 To UBound(bytArr)
'      strArr(i) = Chr(bytArr(i))
'  Next i
  nTotal1 = Timer - sngTime
  sngTime = Timer
 
  ReDim ar1(Len(cLine))
  CopyMemory ByVal VarPtr(ar1(0)), ByVal StrPtr(cLine), Len(cLine) * 2
 
  nTotal2 = Timer - sngTime
 
  Debug.Print nTotal1, nTotal2
 
End Sub

Output on a few runs:
1.146875E-02  0.00975
 0.0091875    0.011375
 9.90625E-03   0.0081875
 0.0113125    9.59375E-03

Ok ;)

I didnt convert to string ....Thats where it costs a lot of time ...


You can use split Command For exapmle:
Dim A() as Byte
Dim B as string
Dim I as Long
Redim A(Len(b)-1)
For i= 1 to len(B)
  A(i-1)=asc(mid(B,I,1))
Next i
Of course if it's Japanese, a conversion to single byte is not going to be nice.  Maybe integer array makes more sense.
Avatar of bsavas

ASKER

Hi RdRunner,
Thanks for your code. It's the nearest code that satisfied me. But, here is the problem;

'definitions:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As Long)
'Private Type dblByte
'   Hi As Byte
'   Lo As Byte
'End Type


Private Sub Form_Load()

    Dim ar1() As Integer
    Dim cLine As String
    cLine = "Test for strings in VB"

    ReDim ar1(Len(cLine))
    CopyMemory ByVal VarPtr(ar1(0)), ByVal StrPtr(cLine), Len(cLine) * 2
    DoEvents
   
End Sub

! This works good with ANSI characters. But what i want is to copy Japanese characters and analyze them one by one. I mean i'll code like after copying Japanese characters into the array;

 for x= 0 to len(Japanese_String)
   if ar1(x) = something... then ...
 next x

so the code above doesnt show japanese characters...
Well i THOUGHT Japanese chars would be 2 byte per character...

Try to output

debug.? chrW(ar1(x))

This should output the character correctly...

I dont know how Japanese characters are stored so i am not quite sure about it... If its Unicode it should work... (i hope)

Well after all i am german and a-z usually do the trick for me ;)
Avatar of bsavas

ASKER

Hi RdRunner,
Thanks for your code. It's the nearest code that satisfied me. But, here is the problem;

'definitions:
Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (pDest As Any, pSrc As Any, ByVal ByteLen As Long)
Private Declare Sub ZeroMemory Lib "kernel32.dll" Alias "RtlZeroMemory" (Destination As Any, ByVal Length As Long)
'Private Type dblByte
'   Hi As Byte
'   Lo As Byte
'End Type


Private Sub Form_Load()

    Dim ar1() As Integer
    Dim cLine As String
    cLine = "Test for strings in VB"

    ReDim ar1(Len(cLine))
    CopyMemory ByVal VarPtr(ar1(0)), ByVal StrPtr(cLine), Len(cLine) * 2
    DoEvents
   
End Sub

! This works good with ANSI characters. But what i want is to copy Japanese characters and analyze them one by one. I mean i'll code like after copying Japanese characters into the array;

 for x= 0 to len(Japanese_String)
   if ar1(x) = something... then ...
 next x

so the code above doesnt show japanese characters...
I am now totally confused as to what is trying to be achieved. The original question was to convert a long string to an array for analyis because Mid was too slow.

Well I've just tried the following code:

Dim s As String

Private Sub Command1_Click()
    Dim t As Single
    Dim l As Long
    Dim c As String
   
    t = Timer
    For l = 1 To Len(s)
        c = Mid(s, l, 1)
    Next
    Debug.Print Timer - t
End Sub

Private Sub Command2_Click()
    Dim t As Single
    Dim l As Long
    Dim b() As Byte
    Dim c As String
   
    t = Timer
    b = s
    For l = 1 To Len(s)
        c = chr(b((l - 1) * 2))
    Next
    Debug.Print Timer - t
End Sub

Private Sub Form_Load()
    Dim l As Long
   
    For l = 0 To 25
        s = s & String(100000, Asc("a") + l)
    Next
End Sub

This uses a far longer string than requested, and on my AMD 1700+ machine it takes just under 1 sec for both methods (actually on average Mid takes very slightly less time).

So, my advice would be to stop complicating the issue and just use Mid
Avatar of bsavas

ASKER

Well, no need to be complicated. Simply, all i want is to make an array from a Japanese text string using CopyMemory function...(No ANSI chars)
The code of RdRunner above works well and fast with ANSI chars. But i want it to be for Japanese characters.
bsavas:
This old question needs to be finalized -- accept an answer, split points, or get a refund.  For information on your options, please click here-> http:/help/closing.jsp#1 
EXPERTS:
Post your closing recommendations!  No comment means you don't care.
Avatar of DanRollins
bsavas, an EE Moderator will handle this for you.
Moderator, my recommended disposition is:

    Refund points and save as a 0-pt PAQ.
    *** useful techniques shown, but DBCS question was not answered.

DanRollins -- EE database cleanup volunteer
ASKER CERTIFIED SOLUTION
Avatar of Computer101
Computer101
Flag of United States of America image

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