How to insert a zero terminator into a specific location in a string

Started by etow, February 24, 2008, 01:41:31 AM

Previous topic - Next topic

etow

Hi

How do you insert a zero terminator character to end the string at any where in the string?

Please help.

Thanks

MichaelW


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      str1 db "my other brother darryl",0
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
; --------------------------------------------------------------------
; This proc inserts a null in the specified string. The pos parameter
; can be viewed as the zero-based position where the null will be
; inserted, or as the length of the truncated string. It is the user's
; responsibiliy to ensure that pos specifies an offset that is within
; the string.
; --------------------------------------------------------------------
szTruncate proc src:DWORD, pos:DWORD
    mov eax, src
    add eax, pos
    mov BYTE PTR [eax], 0
    mov eax, pos            ; return the truncated length
    ret
szTruncate endp
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

    invoke szTruncate, ADDR str1, sizeof str1 - 1
    push eax
    print ADDR str1,9
    pop eax
    print ustr$(eax),13,10

    invoke szTruncate, ADDR str1, sizeof str1 - 2
    push eax
    print ADDR str1,9
    pop eax
    print ustr$(eax),13,10

    invoke szTruncate, ADDR str1, sizeof str1 - 3
    push eax
    print ADDR str1,9
    pop eax
    print ustr$(eax),13,10

    invoke szTruncate, ADDR str1, 3
    push eax
    print ADDR str1,9
    pop eax
    print ustr$(eax),13,10

    invoke szTruncate, ADDR str1, 2
    push eax
    print ADDR str1,9
    pop eax
    print ustr$(eax),13,10

    invoke szTruncate, ADDR str1, 1
    push eax
    print ADDR str1,9
    pop eax
    print ustr$(eax),13,10

    invoke szTruncate, ADDR str1, 0
    push eax
    print ADDR str1,9
    pop eax
    print ustr$(eax),13,10

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start


my other brother darryl 23
my other brother darry  22
my other brother darr   21
my      3
my      2
m       1
        0
eschew obfuscation

etow

Hi Michael,

I have a question for you.

What if I declare and use an array of characters to store the string then how can I modify your code for szTruncate proc to make it work properly?

For example:

I declare:    Local mulitpleAdditions[1024]: Byte
Use the following code:

              [b]mov Ebx, 1
              mov number, 20
             .while Ebx <= number
                  Mov Eax, cat$(Addr mulitipleAdditions, str$(number), " + ")
                  inc Ebx
             .endw[/b]


Thanks

MichaelW

I'm not sure I understand your question. If you are just trying to truncate the string at some byte length, 10 for example, then you could use something like this:

invoke szTruncate, ADDR mulitpleAdditions, 10

Also, I think Append macro would be a better choice for what your code is doing, and to indicate that the buffer is empty you need zero the first byte. Something like this:

    LOCAL multipleAdditions[1024]:BYTE
    mov multipleAdditions, 0

    Append multipleAdditions, "20"
    xor ebx, ebx
    .WHILE ebx < 9
      Append multipleAdditions, " + 20"
      inc ebx
    .ENDW

    print ADDR multipleAdditions,13,10

eschew obfuscation

ToutEnMasm

Hello,
Something not difficult to do,
Quote
invoke lstrlen,addr TheString  ;or other functions to know the lenght of the string
mov ecx,eax
lea edx,TheString
;No you can choose a value between 0 and ecx to put a zero
shr ecx,2     ;for example,divide par 2
mov eax,0
mov byte ptr [edx+ecx],al                 ;troncate the string


etow

Hi Michael,

Let me make sure you understand.  I am sorry that I was so unclear by explaining my question.
In my previous last post I quoted :

Quote from: etow on February 25, 2008, 12:45:56 PM
Hi Michael,

I have a question for you.

What if I declare and use an array of characters to store the string then how can I modify your code for szTruncate proc to make it work properly?

Use the following code:


Local multipleAdditions[1024]: Byte

mov Ebx, 1
mov number, 20
.while Ebx <= number
Mov Eax, cat$(Addr multipleAdditions, str$(number), " + ")
inc Ebx
.endw

 print Addr multipleAdditions




-------------------------------------------------------------------------------


The output for the code above will be:

20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 + 20 +



I want to get rid of the last "+" sign character after the last "20" character outputted to the screen
However, the number of times of the iteration of the loop is unknown if the user enters the number to repeat the loop as many times they want to.

If the appropriate size of the array of characters is unknown to the programmer but you declare a maximum size for the array of characters large enough to use before execution of program and you want to place the null terminator character at the non-null terminator character which is before the current null terminator during your execution of the program, how would you do that by modifying your szTruncate proc code if possible?

How do you calcuate the size of the character array accurately if the array of characters has a very large size of BYTES declared and the program uses least amount of the character array size during program execution and you want the program to accurately place the null character in the correct position in the character array when the maximum size of the character array has not been reached?  Also how would you do that if the maximum array size of the array of characters is reached?

Thanks

MichaelW

The macros and procedures that append the strings to the end of the buffer will place a null at the end of the last string appended. You can eliminate the trailing + by changing the order of the append operations.

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data

    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
testproc proc uses ebx number:DWORD

    LOCAL multipleAdditions[1024]:BYTE

    ; ----------------------------------------------------------
    ; Each time the szCatStr procedure is called it searches for
    ; the first null in the string buffer and appends the new
    ; string there. To ensure that the first new string will be
    ; appended at the start of the string buffer, we need to
    ; put a null there.
    ; ----------------------------------------------------------
   
    mov multipleAdditions, 0

    invoke szCatStr, ADDR multipleAdditions, str$(number)
    mov ebx, number
    dec ebx
    .WHILE ebx
      invoke szCatStr, ADDR multipleAdditions, chr$(" + ")
      invoke szCatStr, ADDR multipleAdditions, str$(number)
      dec ebx
    .ENDW

    print "12345678901234567890123456789012345678901234567890",13,10

    ; ---------------------------------------------------------------
    ; This will display a "z" at the position of the null terminator.
    ; ---------------------------------------------------------------

    print ADDR multipleAdditions,"z",13,10

    print ustr$(len(ADDR multipleAdditions)),13,10,13,10

    ret
testproc endp

start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    invoke testproc, 10
    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start


12345678901234567890123456789012345678901234567890
10 + 10 + 10 + 10 + 10 + 10 + 10 + 10 + 10 + 10z
47


eschew obfuscation

etow

Hi Michael,

I understand what you mean but I still have problems since I don't use szCatStr function.
I am going to post my code and you can help me figure out the solution.


;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;<<  calculateDiv function will return the quotient = Dividend div Divisor  <<
;<<  in Eax register                                                        <<
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

calculateDiv Proc Dividend:DWord, Divisor:DWord
  Local Quotient:DWord
  ; Quotient is the number result from dividing Dividend by Divisor

  Mov Eax, Dividend   ; set Eax = Dividend
  Mov Ebx, Divisor    ; set Ebx = Divisor
  Cdq                 ; convert to double quad word or 64-bit long word
  Div Ebx             ; Eax = Eax / Ebx
  Mov Quotient, Eax   ; set Quotient = Eax
                      ; Quotient = Dividend / Divisor
  Mov Eax, Quotient   ; return Quotient value for calculateDiv function

  Ret
calculateDiv EndP
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;<<  calculateMod function will return the remainder = Dividend mod Divisor <<
;<<  in Eax register                                                        <<
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

calculateMod Proc Dividend:DWord, Divisor:DWord
  Local Remainder:DWord
  ; Remainder is the difference of the dividend and the product of
  ; divisor and quotient

  Mov Eax, Dividend   ; set Eax = Dividend
  Cdq                 ; convert to double quad word or 64-bit long word
  Mov Ebx, Divisor    ; set Ebx = Divisor
  Div Ebx             ; Eax = Eax / Ebx, Edx contains the remainder
                      ; Edx = remainder
  Mov Remainder, Edx  ; set Remainder = Edx
                      ; Remainder = Dividend mod Divisor
  Mov Eax, Remainder  ; return Remainder value for calculateMod function

  Ret
calculateMod EndP
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
;<<  primeFactorizationPowers procedure will factor a positive integer greater  <<
;<<  than or equal to 2 into prime factors with each prime factor having a      <<
;<<  certain power displayed according its factorization on the screen          <<
;<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

primeFactorizationPowers Proc Number:DWord

Local Remainder:DWord
Local Quotient:DWord
Local primePowerCounter:DWord
Local PrimeFactor[1024]:Byte
Local Factor:DWord
    Local previousFactor:BOOLEAN
; Remainder is the difference between the dividend and the product of
;   divisor and quotient
; Quotient is the number result when dividing dividend by divisor
; primePowerCounter will counter the powers of each prime factor
; PrimeFactor is the string containing the factored primes of the user's
;   integer
; Factor is one of the prime factors of the user's number
    ; previousFactor is to check if still on current prime factor

       Mov Factor, 2       ; set Factor = 2
       Mov Esi, Number     ; set Esi = Number

       .If ((Esi <= SDWord Ptr 1) || (Esi > 100000000))
        ; if (Number <= 1) Or (Number > 100000000) then
        ; if Number is less than or equal to 1 OR Number is greater than 100000000
        ; then error
        print chr$(13, 10)
        print chr$("Invalid integer entered!", 13, 10)
        print chr$("An integer must be between 2 and 100000000!", 13, 10)
        print chr$(13, 10, 13, 10)
       .ElseIf ((Esi > 1) && (Esi <= 100000000))
        ; if (Number > 1) and (Number <= 100000000) then
        ; if Number is greater than 1 AND Number less than or equal to 100000000
        ; then

        print chr$("  =  ")  ; output  =  to screen

        Mov PrimeFactor, 0  ; set the PrimeFactor string to empty
         Mov primePowerCounter, 0 ; set primePowerCounter = 0
         Mov previousFactor, FALSE ; set previousFactor = FALSE

         .While Factor <= Esi ; while Factor less than or equal to (Number = Esi) do
           Invoke calculateDiv, Number, Factor ; call calculateDiv function
                                               ; quotient = Number div Factor
           Mov Quotient, Eax                   ; Eax = Number div Factor
                                               ; set Quotient = Eax
           Invoke calculateMod, Number, Factor ; call calculateMod function
                                               ; remainder = Number mod Factor
           Mov Remainder, Eax                  ; Eax = Number mod Factor
                                               ; set Remainder = Eax
           .If (Remainder == 0)  ; if Remainder equals to zero then
              .If (previousFactor == FALSE)
               ; if previousFactor equals to FALSE then
            Mov Ebx, cat$(Addr PrimeFactor, str$(Factor), "^")
            ; insert the prime factor in the PrimeFactor string with the power
            ; sign
            Inc primePowerCounter ; primePowerCounter = primePowerCounter + 1
            Mov previousFactor, TRUE ; set previousFactor = TRUE
              .ElseIf (previousFactor == TRUE)
              ; if previousFactor equals to TRUE then
                Inc primePowerCounter ; primePowerCounter = primePowerCounter + 1
              .EndIf

               Mov Ecx, Quotient   ; set Ecx = Quotient
               Mov Number, Ecx     ; set Number = Quotient = Ecx

           .ElseIf
              .If (primePowerCounter != 0)
              ; if primePowerCounter is not equal to 0 then
                 Mov Ebx, cat$(Addr PrimeFactor, str$(primePowerCounter), " * ")
                 ; concatenate the power of the prime factor to PrimeFactor string
              .EndIf

              Mov primePowerCounter, 0   ; set primePowerCounter = 0
              Mov previousFactor, FALSE  ; set previousFactor = FALSE
              Inc Factor   ; Factor = Factor + 1
           .EndIf
         .EndW

         print Addr PrimeFactor ; print PrimeFactor string

         print chr$(13, 10, 13, 10, 13, 10)
         ; output three carriage return line feeds to the screen
         Mov PrimeFactor, 0  ; reinitialize the PrimeFactor string by setting
                             ; this string to empty
       .EndIf
   Ret
primeFactorizationPowers EndP


----------------------------------------------------------------------------------------------------------------------------------
if user enters 144 for Number, then the output for primeFactorizationPowers procedure will be
  = 2^4 * 3^2 *

if user enters 256 for Number, then the output for primeFactorizationPowers procedure will be
  = 2^8 *

if user enters 14 for Number, then the output for primeFactorizationPowers procedure will be
  = 2^1 * 7^1 *

Do you get the pattern?.  Each time an extra asterisk is displayed at the end of the string when the program runs and I want that extra askerisk be removed by putting a zero terminator character at that position in the string.

MichaelW

I was suggesting that you change the program logic so the * is not added to the end of the string, but is instead added before the next factor. But if it's easier to just remove it, and you know for sure that it will always be there, then perhaps something like this:

mov eax, len(ADDR PrimeFactor)
dec eax
invoke szTruncate, ADDR PrimeFactor, eax

eschew obfuscation

etow

Hi Michael,

Thanks for your help.  I got it to work.

I have another question for you.

How do you compare each individual characters in a string that are "0" to non "0" characters in a real or floating point number string?

Thanks

etow

Hi Michael,

When I ask about comparing each digiit character in a real or floating point string so that I need to put a null character terminator where the last zero digit character is before a non-zero digit character

I wonder how is this done?

Thanks

MichaelW

OK, now I understand the question. One easy solution is to use the CRT strrchr function.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    include \masm32\include\masm32rt.inc
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    .data
      nstr db "12345678901234",0
    .code
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
start:
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
    print ADDR nstr,13,10

    ; ----------------------------------------------------------------
    ; The strrchr function returns a pointer to the last occurrence of
    ; the specified character in the string, or NULL if the character
    ; is not found.
    ; ----------------------------------------------------------------
   
    invoke crt_strrchr, ADDR nstr, "0"
    mov ebx, eax
    .IF ebx
      print ebx,13,10           ; display starting at the character
      mov BYTE PTR [ebx], 0     ; insert the null
      print ADDR nstr,13,10     ; display the modified string
    .ELSE
      print "not found",13,10
    .ENDIF

    inkey "Press any key to exit..."
    exit
; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««
end start

eschew obfuscation

etow