Seite 1 von 1

HexDebug()

Verfasst: 22.12.2015 15:46
von Domino
Einer geht noch: HexDebug() gibt für unbekannte Zeichenketten eine hexadezimale Darstellung und den Klartext rechts daneben als normalen Text zurück. Nützlich, um festzustellen, ob LF oder CR statt Leerzeichen verwendet werden.

Code: Alles auswählen

EnableExplicit
Declare.s HexDebug(Text.s)
Debug HexDebug("aB© äöüſßÄÖÜ"+#LF$+#CR$+"()[]")

;{
Procedure.s HexDebug(Text.s)
  Define Ausgabe.s = ""
  Define Adrlen.l  = 0
  Define Pos1.l    = 0
  Define Pos2.l    = 0
  Define Nibble.l  = 0
  Define i.l, Zeichen.l
  Define HexTab.s  = "0123456789abcdef"
  If StringByteLength(Text, #PB_Ascii) <= 256
    Adrlen = 2
  ElseIf StringByteLength(Text, #PB_Ascii) <= 65536
    Adrlen = 4
  ElseIf StringByteLength(Text, #PB_Ascii) <= 16777216
    Adrlen = 6
  Else
    Adrlen = 8
  EndIf
  For Pos1 = 0 To Int((StringByteLength(Text, #PB_Ascii)-1)/16)*16 Step 16
    For i=Adrlen-1 To 0 Step -1
      Nibble = (Pos1 >> (i*4)) & $000f
      Ausgabe + Mid(HexTab, Nibble+1, 1)
    Next i
    Ausgabe + ": "
    For Pos2=0 To 15
      If Pos1+Pos2 <= StringByteLength(Text, #PB_Ascii)
        Zeichen = Asc(Mid(Text, Pos1+Pos2+1, 1))
        Nibble = (Zeichen >> 4) & $000f
        Ausgabe + Mid(HexTab, Nibble+1, 1)
        Nibble = Zeichen & $000f
        Ausgabe + Mid(HexTab, Nibble+1, 1) + " "
      Else
        Ausgabe + "   "
      EndIf
    Next Pos2
    Ausgabe + " "
    For Pos2=0 To 15
      If Pos1+Pos2 <= StringByteLength(Text, #PB_Ascii)
        Zeichen = Asc(Mid(Text, Pos1+Pos2+1, 1))
        If Zeichen < $0020
          Zeichen = $002e
        ElseIf Zeichen >= $0080 And Zeichen < $00a0
          Zeichen = $002e
        EndIf
        Ausgabe + Chr(Zeichen)
      Else
        Ausgabe + " "
      EndIf
    Next Pos2
    Ausgabe + #LF$
  Next Pos1
  ProcedureReturn Ausgabe
EndProcedure
;}
Ergibt:

Code: Alles auswählen

00: 61 42 a9 20 e4 f6 fc 7f df c4 d6 dc 0a 0d 28 29  aB© äöüſßÄÖÜ..()
10: 5b 5d                                            []              
Ggf. Unicode-Option in den Compiler-Einstellungen anpassen!

Re: HexDebug()

Verfasst: 22.12.2015 16:19
von Kurzer
Domino, du hattest in einem anderen Thread geschrieben, dass Du recht neu bei PB bist (sehr wohl aber schon länger programmierst). Ich würde dir trotzdem empfehlen einfach mal eine Weile die PB Hilfe zu studieren. Du wirst merken, dass einiges auch anders und einfacher geht.

Hast Du Dir z.B. mal den Hex() Befehl aus der String-Lib angesehen?

Deine Prozedur würde sich damit vereinfachen lassen:

Code: Alles auswählen

Procedure.s HexDebugEinfach(sText.s)
	Protected.s sHexcodes ="    ", sChars, sChar
	Protected.i i, iChar
	
	For i = 1 To Len(sText)
		sChar = Mid(sText, i, 1)
		iChar = Asc(sChar)
		sHexcodes + Right("0" + Hex(iChar, #PB_Byte), 2) + " "
		If iChar < 32 Or iChar > 159
			sChar = "."
		EndIf
		sChars + sChar
	Next i
	
	ProcedureReturn sHexcodes + #TAB$ + sChars	
EndProcedure
Das ist jetzt keine 1:1 Nachbildung, es soll nur zeigen, dass Du Dich um die Umwandlung nicht wirklich selbst kümmern musst.

Finde erstmal das Potential von PB heraus bevor Du Dir irgendwelche Hilfsprozeduren schreibst. ;)

Edit: Ach verdammt, hat mir nun doch keine Ruhe gelassen diese Aufteilung in mehrere Zeilen.

Code: Alles auswählen

EnableExplicit

Procedure.s HexDebugEinfach(sText.s, iBytePerRow.i)
	Protected.s sOutput, sHexcodes, sChars, sChar
	Protected.i i, iChar
	
	For i = 0 To Len(sText) - 1
		; Umbrechen in neue Zeile nach iBytePerRow Zeichen
		If i % iBytePerRow = 0 And i >  0
			sOutput + Right("0" + Hex(i-iBytePerRow, #PB_Byte), 2) + ": " + sHexcodes + #TAB$ + sChars + #CR$ + #LF$
			sHexcodes = "" : sChars = ""
		EndIf
		
		; Umwandlung in Hexziffer
		sChar = Mid(sText, i + 1, 1)
		iChar = Asc(sChar)
		sHexcodes + Right("0" + Hex(iChar, #PB_Byte), 2) + " "
		
		; Ausfiltern nicht druckbarer Zeichen
		If iChar < 32 Or iChar > 159
			sChar = "."
		EndIf
		sChars + sChar
	Next i
	
	; Letzte Zeile formatieren
	sOutput + Right("0" + Hex(i-Len(sChars), #PB_Byte), 2) + ": " + sHexcodes + Space (((iBytePerRow - (i % iBytePerRow)) % iBytePerRow) * 3) + #TAB$ + sChars + #CR$ + #LF$
	ProcedureReturn sOutput   
EndProcedure

Debug HexDebugEinfach("aB© äöüſßÄÖÜsöldf askfjapw4rc j4jrcp a84cuj ap4wrf"+#LF$+#CR$+"()[]43567", 8)

Re: HexDebug()

Verfasst: 22.12.2015 18:55
von mk-soft
Oder so

Code: Alles auswählen

Procedure.s DebugString(text.s, spalten.i = 8)
  Protected *pString.character
  Protected spalte.i
  Protected hex.s, ausgabe.s, result.s
  *pString = @text
  While *pString\c <> 0
    CompilerIf #PB_Compiler_Unicode = 1
      hex + RSet(Hex(*pString\c, #PB_Unicode), 4, "0") + " "
    CompilerElse
      hex + RSet(Hex(*pString\c, #PB_Ascii), 2, "0") + " "
    CompilerEndIf
    If *pString\c >= 32
      ausgabe + Chr(*pString\c)
    Else
      ausgabe + "."
    EndIf
    *pString + SizeOf(character)
    spalte + 1
    If spalte > spalten
      spalte = 0
      hex + " | " + ausgabe + #LF$
      ausgabe = ""
    EndIf
  Wend
  If ausgabe
    hex + " | " + ausgabe
  EndIf
  
  ProcedureReturn hex
  
EndProcedure

t1.s = "aB© äöüſßÄÖÜ"+#LF$+#CR$+"()[]"
Debug DebugString(t1)

Re: HexDebug()

Verfasst: 22.12.2015 20:20
von Kurzer
Das hier dürfte mit Abstand die kürzeste Version sein. :mrgreen:

Code: Alles auswählen

EnableExplicit
Define sText.s = "dlkfjsijgisvjgirtgjreikgöskgkosäd"
ShowMemoryViewer(@sText, StringByteLength(sText))

Re: HexDebug()

Verfasst: 22.12.2015 20:57
von Nino
Kurzer hat geschrieben:Das hier dürfte mit Abstand die kürzeste Version sein. :mrgreen:
:-)

Re: HexDebug()

Verfasst: 22.12.2015 21:26
von mk-soft
:mrgreen: :allright:

Re: HexDebug()

Verfasst: 22.04.2016 16:43
von Sicro
@mk-soft:
Dein Code wird in CodeArchiv unter Debug/DebugHex.pbi aufgenommen.
Ich habe mich für dein Code entschieden, weil er zusätzlich auch Unicode-Zeichen unterstützt.

Re: HexDebug()

Verfasst: 24.03.2017 10:25
von PeDre
Ich habe beim Umstieg von Xojo eine ähnliche Prozedur, wie die hier im Thread, direkt nach PureBasic übernommen. Es gibt zusätzlich Parameter für die Formatierung der Ausgabe.
Eine zweite Version davon kommt jetzt ohne Strings aus, und ist in der kompilierten Exe um einiges schneller. Beide Prozeduren funktionieren mit Ascii und Unicode, habe sie aber nur mit x86 Versionen getestet.

Peter

Code: Alles auswählen

EnableExplicit

Procedure.s MemoryFormatHexALT(*Memory, iLength.i, iColumns.i = 16, iOffset.i = 0, fWithAscii.i = #True)
   ; Gibt die Daten formatiert als Hexadezimal und ASCII-Zeichen aus.
   ; -> *Memory:     Die Adresse des Speicherbereichs.
   ; -> iLength:     Die Länge des Speicherbereichs.
   ; -> iColumns:    Die Anzahl der Bytes die pro Zeile formatiert werden.
   ; -> iOffset:     Der Offset ab der die Daten verwendet werden sollen.
   ; -> fWithAscii:  Bei False wird nur die Hexanzeige ohne der ASCII-Darstellung formatiert.
   ; Rückgabe:       Die Zeichenfolge mit den formatierten Daten.
   ;                 z.B. bei 16 Zeichen pro Zeile:
   ;                 00000| 37 BA 17 CC 28 F9 E3 F0 46 79 15 C2 4B 94 13 ED |7º.Ì(ùãðFy.ÂK..í|
   Protected sReturn.s, fResult.i
   Protected iDataSize.i, iChar.i, c.i, sChar.s, sHex.s, sAscii.s, sRow.s
   #iColumnsMin = 1
   #iColumnsMax = 64
   
   ; Übergebene Parameter prüfen.
   fResult = Bool(*Memory And iLength)
   If (Not fResult)
      ;DebugView::Error("Der Zeiger oder die Länge des Speichers ist Null. *Memory = " + *Memory + ", iLength = " + iLength)
   EndIf
   If fResult
      iDataSize = iLength - 1
      ; Den Offset prüfen.
      If ((iOffset > iDataSize) Or (iOffset < 0))
         fResult = #False
         ;DebugView::Error("Der Offset ist ungültig. iOffset = " + iOffset + ", iLength = " + iLength)
      EndIf
   EndIf
   If fResult
      If ((iColumns < #iColumnsMin) Or (iColumns > #iColumnsMax))
         fResult = #False
         ;DebugView::Error("Die Anzahl der Spalten ist ungültig. iColumns = " + iColumns)
      EndIf
   EndIf
   
   If fResult
      ; Daten formatieren.
      For c = iOffset To iDataSize
         
         ; iColumns-Bytes pro Zeile.
         If ((c - iOffset) % iColumns) = 0 And ((c - iOffset) > 0)
            sRow = Hex(c - iColumns)
            sRow = Mid("0000", Len(sRow)) + sRow
            If fWithAscii
               sReturn + sRow + "|" + sHex + "|" + sAscii + "|" + #CRLF$
            Else
               sReturn + sRow + "|" + sHex + "|" + #CRLF$
            EndIf
            sHex = #Empty$
            sAscii = #Empty$
         EndIf
         
         ; Byte einlesen.
         iChar = PeekA(*Memory + c)
         sChar = Hex(iChar)
         ; Hex-Ausgabe des Bytes.
         If Len(sChar) = 2
            sHex + sChar + " "
         Else
            sHex + "0" + sChar + " "
         EndIf
         
         ; ASCII-Ausgabe.
         If fWithAscii
            If (((iChar > 31) And (iChar < 128)) Or ((iChar > 160) And Not (iChar = 173)))
               sAscii + Chr(iChar)
            Else
               sAscii + "."
            EndIf
         EndIf
      Next c
      
      ; Letzte Zeile mit restlichen Bytes formatieren.
      ; Nur wenn überhaupt restliche Bytes vorhanden sind.
      If (((c - iOffset) % iColumns) > 0)
         sRow = Hex((c / iColumns) * iColumns)
         sRow = Mid("0000", Len(sRow)) + sRow
         If fWithAscii
            sReturn + sRow + "|" + sHex + Space((iColumns - (c % iColumns)) * 3) + "|" + sAscii +
                      Space(iColumns - (c % iColumns)) + "|"
         Else
            sReturn + sRow + "|" + sHex + Space((iColumns - (c % iColumns)) * 3) + "|" + "|"
         EndIf
         
      ; Es ist nur noch eine Zeile mit genau der Anzahl an Bytes wie 'iColumnChars' vorhanden.
      Else
         sRow = Hex(c - iColumns)
         sRow = Mid("0000", Len(sRow)) + sRow
         If fWithAscii
            sReturn + sRow + "|" + sHex + "|" + sAscii + "|"
         Else
            sReturn + sRow + "|" + sHex + "|"
         EndIf
      EndIf
   EndIf
   
   ProcedureReturn sReturn
EndProcedure


Procedure.s MemoryFormatHex(*Memory, iLength.i, iColumns.i = 16, iOffset.i = 0, fWithAscii.i = #True)
   ; Gibt die Daten formatiert als Hexadezimal und ASCII-Zeichen aus.
   ; -> *Memory:     Die Adresse des Speicherbereichs.
   ; -> iLength:     Die Länge des Speicherbereichs.
   ; -> iColumns:    Die Anzahl der Bytes die pro Zeile formatiert werden.
   ; -> iOffset:     Der Offset ab der die Daten verwendet werden.
   ; -> fWithAscii:  Bei False wird nur die Hexanzeige ohne der ASCII-Darstellung formatiert.
   ; Rückgabe:       Die Zeichenfolge mit den formatierten Daten.
   ;                 z.B. bei 16 Zeichen pro Zeile:
   ;                 00000| 37 BA 17 CC 28 F9 E3 F0 46 79 15 C2 4B 94 13 ED |7º.Ì(ùãðFy.ÂK..í|
   Protected sReturn.s, fResult.i, iDataSize.i, iDataPos.i, c.i, d.i, iChar.a, iPos.i
   Protected *sBuffer, iBufferLength.i, iBufferPos.i, iColumnChars.i, iColumn.i, iRow.i
   #iAddressLength = 5
   #iColumnsMin = 1
   #iColumnsMax = 64
   #iCharPeriod = 46
   #iCharSpace = 32
   #iCharPipe = 124
   
   ; Übergebene Parameter prüfen.
   fResult = Bool(*Memory And iLength)
   If (Not fResult)
      ;DebugView::Error("Der Zeiger oder die Länge des Speichers ist Null. *Memory = " + *Memory + ", iLength = " + iLength)
   EndIf
   If fResult
      iDataSize = iLength - 1
      ; Den Offset prüfen.
      If ((iOffset > iDataSize) Or (iOffset < 0))
         fResult = #False
         ;DebugView::Error("Der Offset ist ungültig. iOffset = " + iOffset + ", iLength = " + iLength)
      EndIf
   EndIf
   If fResult
      If ((iColumns < #iColumnsMin) Or (iColumns > #iColumnsMax))
         fResult = #False
         ;DebugView::Error("Die Anzahl der Spalten ist ungültig. iColumns = " + iColumns)
      EndIf
   EndIf
   
   ; Speicher für den formatierten String reservieren.
   If fResult
      ; Die Anzahl der Zeichen pro Zeile berechnen. Plus 2 Zeichen für den Zeilenumbruch.
      If fWithAscii
         iColumnChars = (#iAddressLength + 1 + 1 + (iColumns * 3) + 1 + iColumns + 1 + 2) * SizeOf(Character)
      Else
         iColumnChars = (#iAddressLength + 1 + 1 + (iColumns * 3) + 2) * SizeOf(Character)
      EndIf
      
      iBufferLength = (iLength - iOffset) / iColumns        ; Anzahl der vollständigen Zeilen.
      iBufferLength + Bool((iLength - iOffset) % iColumns)  ; Plus 1 für letzte unvollständige Zeile wenn vorhanden.
      iBufferLength * iColumnChars + SizeOf(Character)      ; Zeilen mal der Anzahl der formatierten Zeichen plus #Null.
      *sBuffer = AllocateMemory(iBufferLength)
      If (Not *sBuffer)
         fResult = #False
         ;DebugView::Error("Der Speicher konnte nicht alloziert werden.")
      EndIf
   EndIf
   
   ; Die Daten formatieren.
   If fResult
      iRow = 1
      For iDataPos = iOffset To iDataSize
         ; Zähler für die Zeilen, Spalten und Schreibposition berechnen.
         iColumn + 1
         If (iColumn > iColumns)
            iColumn = 1
            iRow + 1
            ; Schreibposition anpassen für eine neue Zeile.
            If fWithAscii
               iBufferPos + ((1 + iColumns + 1) * SizeOf(Character))
            EndIf
            iBufferPos + (2 * SizeOf(Character)) ; Den Zeilenumbruch berücksichtigen.
         EndIf
         
         ; Die Adresse am Anfang der Zeile ausgeben. Die Adresse ist iDataPos von der For-Next Schleife.
         If (iColumn = 1)
            For c = (#iAddressLength - 1) To 0 Step -1
               iChar = (iDataPos >> (c * 4)) & $0F
               If (iChar < $0A)
                  iChar + 48  ; plus Ascii-Wert von '0'.
               Else
                  iChar + 55  ; plus Ascii-Wert von 'A' - 10.
               EndIf
               PokeA(*sBuffer + iBufferPos, iChar)
               iBufferPos + SizeOf(Character)
            Next c
            ; Pipe und ein Leerzeichen ausgeben.
            PokeA(*sBuffer + iBufferPos, #iCharPipe)
            iBufferPos + SizeOf(Character)
            PokeA(*sBuffer + iBufferPos, #iCharSpace)
            iBufferPos + SizeOf(Character)
         EndIf
         
         ; Die Hexzahl für ein Byte ausgeben.
         For d = 1 To 0 Step -1
            iChar = (PeekA(*Memory + iDataPos) >> (d * 4)) & $0F
            If (iChar < $0A)
               iChar + 48  ; plus Ascii-Wert von '0'.
            Else
               iChar + 55  ; plus Ascii-Wert von 'A' - 10.
            EndIf
            PokeA(*sBuffer + iBufferPos, iChar)
            iBufferPos + SizeOf(Character)
         Next d
         ; Anschließend ein Leerzeichen ausgeben.
         PokeA(*sBuffer + iBufferPos, #iCharSpace)
         iBufferPos + SizeOf(Character)
         
         ; Das Ascii-Zeichen ausgeben.
         If fWithAscii
            iChar = PeekA(*Memory + iDataPos)
            If Not (((iChar > 31) And (iChar < 128)) Or ((iChar > 160) And Not (iChar = 173)))
               iChar = #iCharPeriod
            EndIf
            ; Position des Zeichen im Buffer berechnen.
            iPos = iBufferPos + ((((iColumns - iColumn) * 3) + iColumn) * SizeOf(Character))
            PokeA(*sBuffer + iPos, iChar)
         EndIf
         
         ; Die Pipezeichen für die Ascii-Spalte ausgeben.
         If fWithAscii
            If ((iColumn = iColumns) Or (iDataPos = iDataSize))
               ; In der letzten Zeile die Nullzeichen mit Leerzeichen auffüllen.
               If ((iDataPos = iDataSize) And (iColumn < iColumns))
                  For d = iBufferPos To (iBufferLength - (SizeOf(Character) * 2)) Step SizeOf(Character)
                     If (PeekA(*sBuffer + d) = #Null)
                        PokeA(*sBuffer + d, #iCharSpace)
                     EndIf
                  Next d
               EndIf
               ; Position des Zeichen im Buffer berechnen.
               iPos = iBufferPos + (((iColumns - iColumn) * 3) * SizeOf(Character))
               PokeA(*sBuffer + iPos, #iCharPipe)
               iPos + ((iColumns + 1) * SizeOf(Character))
               PokeA(*sBuffer + iPos, #iCharPipe)
            EndIf
         EndIf
         
         ; Den Zeilenumbruch ausgeben.
         If ((iColumn = iColumns)  Or (iDataPos = iDataSize))
            iPos = (iRow * iColumnChars) - (2 * SizeOf(Character))
            PokeA(*sBuffer + iPos, #CR)
            PokeA(*sBuffer + iPos + SizeOf(Character), #LF)
         EndIf
      Next iDataPos
   EndIf
   
   ; Den formatierten String zurückgeben und den Speicher freigeben.
   If fResult
      sReturn = PeekS(*sBuffer, iBufferLength)
      FreeMemory(*sBuffer)
   EndIf
   
   ProcedureReturn sReturn
EndProcedure


; ------ TEST ------
Define c.i, sTest.s, *sBuffer, iTime.q

If OpenWindow(0, #PB_Ignore, #PB_Ignore, 600, 600, "MemoryFormatHex", #PB_Window_SystemMenu)
   EditorGadget(1, 0, 0, 600, 600)
   SetGadgetFont(1, FontID(LoadFont(#PB_Any, "Courier New", 9)))
   
   *sBuffer = AllocateMemory(10000)
   RandomData(*sBuffer, 10000)

   sTest = "123456789012345678901234567890"
   AddGadgetItem(1, -1, sTest)
   AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest)))
   AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest), 22, 4, #False))
   AddGadgetItem(1, -1, #Null$)
   sTest = "AZaz" + #CRLF$
   AddGadgetItem(1, -1, "AZaz + #CRLF$")
   AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest), 6))
   sTest = #Null$
   AddGadgetItem(1, -1, "#Null$")
   AddGadgetItem(1, -1, MemoryFormatHex(@sTest, StringByteLength(sTest)))
   AddGadgetItem(1, -1, "RandomData")
   AddGadgetItem(1, -1, MemoryFormatHex(*sBuffer, 71))
   
   AddGadgetItem(1, -1, "Speedtest")
   AddGadgetItem(1, -1, #Null$)
   For c = 1 To 5
      iTime = ElapsedMilliseconds()
      sTest = MemoryFormatHexALT(*sBuffer, c * 1000)
      iTime = ElapsedMilliseconds() - iTime
      AddGadgetItem(1, -1, "String " + Str(c * 1000) + " - " + iTime)
   Next c
   AddGadgetItem(1, -1, #Null$)
   For c = 1 To 5
      iTime = ElapsedMilliseconds()
      sTest = MemoryFormatHex(*sBuffer, c * 1000)
      iTime = ElapsedMilliseconds() - iTime
      AddGadgetItem(1, -1, "Memory " + Str(c * 1000) + " - " + iTime)
   Next c
   AddGadgetItem(1, -1, #Null$) 
   AddGadgetItem(1, -1, MemoryFormatHex(*sBuffer, 10000))
   FreeMemory(*sBuffer)

   Repeat : Until (#PB_Event_CloseWindow = WaitWindowEvent())
EndIf