Ir al contenido


Foto

Números gigantes a Hexadecimal y visceversa


  • Por favor identifícate para responder
16 respuestas en este tema

#1 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 30 agosto 2011 - 04:42

Buenas tardes, tengo el siguiente problema, cuando genero un GUID, quitando los guiones y llaves me queda un hexadecimal y requiero convertirlo a un número y de un número igualmente grande requiero convertirlo a hexadecimal, de antemano, muchas gracias.
  • 0

#2 Wilson

Wilson

    Advanced Member

  • Moderadores
  • PipPipPip
  • 2.137 mensajes

Escrito 30 agosto 2011 - 04:57

Prueba con esta función, al menos para un sentido.


delphi
  1. unit Unit1;
  2.  
  3.  
  4. interface
  5.  
  6.  
  7. uses
  8.   Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  9.   Dialogs, StdCtrls;
  10.  
  11.  
  12. type
  13.   TForm1 = class(TForm)
  14.     Button1: TButton;
  15.     procedure Button1Click(Sender: TObject);
  16.   private
  17.     { Private declarations }
  18.   public
  19.     { Public declarations }
  20.   end;
  21.  
  22.  
  23. var
  24.   Form1: TForm1;
  25.  
  26.  
  27. implementation
  28.  
  29.  
  30. {$R *.dfm}
  31. function StrToHex(s : string) : LongWord;
  32. var
  33.   bb : Byte;
  34.   cc : Char;
  35. begin
  36. Result := 0;
  37. s := UpperCase(s);
  38. for bb := 1 to Length(s) do
  39. begin
  40.   Result := Result * 16;
  41.   cc := s[bb];
  42.   case cc of
  43.   '0'..'9': Inc(Result, ord(cc) - ord('0'));
  44.   'A'..'F': Inc(Result, ord(cc) - ord('A')+10);
  45.   else raise EConvertError.Create('Tu error');
  46.   end;
  47. end;
  48. end;
  49.  
  50.  
  51.  
  52.  
  53.  
  54.  
  55. procedure TForm1.Button1Click(Sender: TObject);
  56. begin
  57. ShowMessage(IntToStr(StrToHex('BBA1'))); //  48033
  58. end;
  59.  
  60.  
  61. end.


  • 0

#3 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 30 agosto 2011 - 07:45

Mira a ver si esto te ayuda:



delphi
  1. function StringToGUID(S: String): TGUID;
  2. var
  3.   i: integer;
  4. begin
  5.   Result.Data1:= StrToInt('$'+Copy(S,  2,  8));
  6.   Result.Data2:= StrToInt('$'+Copy(S, 11, 4));
  7.   Result.Data3:= StrToInt('$'+Copy(S, 16, 4));
  8.   for i:=0 to 1 do
  9.     Result.Data4[i]:= BYTE(StrToInt('$'+Copy(S, 21+i*2, 2)));
  10.   for i:=2 to 7 do
  11.     Result.Data4:= BYTE(StrToInt('$'+Copy(S, 22+i*2, 2)));
  12. end;
  13.  
  14.  
  15. function GUIDToString(guid: TGUID): String;
  16. var
  17.   i: integer;
  18. begin
  19.   Result:= '{' + IntToHex(guid.Data1, 4) +
  20.             '-' + IntToHex(guid.Data2, 2) +
  21.             '-' + IntToHex(guid.Data3, 2) +
  22.             '-';
  23.   for i:=0 to 1 do
  24.       Result:= Result + IntToHex(guid.Data4, 2);
  25.   Result:= Result + '-';
  26.   for i:=2 to 7 do
  27.       Result:= Result + IntToHex(guid.Data4, 2);
  28.   Result:= Result + '}';
  29. end;



Ejemplo de uso:


delphi
  1. var
  2.   G: TGUID;
  3.   S: String;
  4. begin
  5.   G:= StringToGUID('{2A8DEC8D-934E-4FF8-825A-05A800047649}');
  6.   S:= GUIDToString(G);
  7. end;



No incorpora comprobación de errores, pero si entiendes el código no te será difícil incorporarla.
               
Saludos.
  • 0

#4 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 31 agosto 2011 - 07:56

Caramba, si hubiese buscado antes de responder hubiese respondido de otra forma    :( .

¡Lazarus tiene las dos funciones que propuse arriba y con el mismísimo nombre!:

StringToGUID
GUIDToString

PD. Código fuente en sysuintf.inc

Saludos.
  • 0

#5 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 31 agosto 2011 - 09:25

Gracias por responder, ilustro mejor mi necesidad, tengo un GUID como este {E80BE677-59E9-4355-9A65-F02FAC6F6E83}, si quito las llaves y los guiones me queda algo como E80BE67759E943559A65F02FAC6F6E83, inicialmente cada bloque de 8,4,4,2,2,2,2,2,2,2,2 bytes, tiene un valor numérico diferente, pero si tomo todo el conjunto hexadecimal de 32 bytes me debe dar una valor numérico conjunto, supongo que si tomara el valor numérico y lo convierto a hexadecimal me debería volver a dar E80BE67759E943559A65F02FAC6F6E83 y finalmente volver a pasarlo a GUID, espero haberme explicado mejor.
  • 0

#6 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 31 agosto 2011 - 09:46

Por lo que entiendo, la funcione que te ha dado Wilson hace eso exactamente, el resultado es el numero "autentico" resultante de convertir el hex a un int, pero en cualquier caso, ese hex to int deberia usar un tipo de entero que no creo que tengas a tu disposicion: un LongWord quizas no te sea suficiente, igual lo pruebas y te da un error a mitad de la conversion porque el result es de tipo LowWord y llegas a su limite, o peor aun, sobrepasas el limite y el resultado se "resetea" sin darte un error.

¿Ese entero "autentico" lo necesitas para algo? ¿No te valdria igual un string que hiciese la misma funcion de almacenar ese GUI?


  • 0

#7 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 31 agosto 2011 - 09:53

Hola Sergio, si debe ser un número autentico, aunque ya se sabe que no hay un tipo de dato que soporte tal cantidad de caracteres, casi 50 si no estoy mal, si obtengo el valor númerico de D1,D2,D3,D4[0],D4[1],D4[2],D4[3],D4[4],D4[5],D4[6],D4[7] quedo con esto 113403348216831948512915122317315125024940 lo que no me sale es convertir nuevamente ese resultado al GUID sin llaves y sin guiones, ya que el valor numérico varía entre 39 y 43 caracteres, por las pruebas que he hecho, alguna sugerencia?
  • 0

#8 escafandra

escafandra

    Advanced Member

  • Administrador
  • 4.107 mensajes
  • LocationMadrid - España

Escrito 31 agosto 2011 - 10:11

No existe un tipo nativo de 128 bits. Cuando surge la necesidad se define el tipo nuevo en una estructura.

Lazarus define TGuid como:


delphi
  1. TGuid = packed record
  2.           case integer of
  3.             1 : (
  4.                   Data1 : DWord;
  5.                   Data2 : word;
  6.                   Data3 : word;
  7.                   Data4 : array[0..7] of byte;
  8.                 );
  9.             2 : (
  10.                   D1 : DWord;
  11.                   D2 : word;
  12.                   D3 : word;
  13.                   D4 : array[0..7] of byte;
  14.                 );
  15.             3 : ( { uuid fields according to RFC4122 }
  16.                   time_low : dword;        // The low field of the timestamp
  17.                   time_mid : word;                      // The middle field of the timestamp
  18.                   time_hi_and_version : word;          // The high field of the timestamp multiplexed with the version number
  19.                   clock_seq_hi_and_reserved : byte;    // The high field of the clock sequence multiplexed with the variant
  20.                   clock_seq_low : byte;                // The low field of the clock sequence
  21.                   node : array[0..5] of byte;          // The spatially unique node identifier
  22.                 );
  23.       end;



¿Por qué no usar el tipo ya previsto?. Esa fue la razón de mi respuesta...

Y ahora me pregunto: ¿Por qué no te sirve?


Saludos.
  • 0

#9 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 31 agosto 2011 - 10:19

Resulta que han diseñado todo un modelo de base de datos en oracle, y todos los campos claves son NUMERIC(12,0), pues bien en las pruebas se está presentando problemas de claves repetidas, puesto que hay modelos desconectados y demás, entonces hacer el cambio en la base de datos para guardar el GUID es muy difícil, ya que hay gran cantidad de desarrollos que usan el tipo NUMERIC(12,0), resolvieron que si dejan el campo como NUMERIC se puede almacenar un entero de gran capacidad de caracteres y si sumamos que el GUID es único, con esto no habría claves repetidas e implicaría menos cambios tanto en el modelo como en el desarrollo.
  • 0

#10 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 31 agosto 2011 - 10:38

Podrias usar una libreria de interos "muy grandes" como esta http://sourceforge.n...ects/bigint-dl/... pero ¿has pensado que el numeor que buscas tiene mas de 12 cifras? No te va a caber en el campo de la base de datos!

Pero viendo para lo que lo quieres usar, quizas seria mejor usar una funcion de hash sobre el GUID en modo string, por ejemplo MD5 o SHA1, eso te daria otro string "mezclando todo", ahora recortarias las primeras 6 o 7 letras, la conviertes de array of word (los char individuales) a int (el int del primero, *256, mas el int del segundo, *256, etc) y al final lo que consigues es acortar la longitud del numero resultante (a mas cifras, uses del hash, mayor numero obtienes, claro, no se si los 6 o 7 que te proponia seran demasiados o no) sin que deje de ser unico... bueno, en teoria podria repetirse un entero, pero la probabilidad es del orden de 1/10^12 (al ser numeros de 12 cifras), es decir, un 0.0000000001% de que dos GUID se repitan y "choquen", bastante asumible... sobre todo porque usando numeric(12,0) no puedes aspirar a más!
  • 0

#11 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 31 agosto 2011 - 10:53

de hecho en la base de datos cambiando el tipo de NUMERIC(12,0) a NUMERIC deja ingresarle efectivamente el numero que quiero, que me está restringiendo es el lenguaje, ya que cuando consulte lo que obtendré es el numero gigante y no tengo como tratarlo, revisaré el link y te cuento.
  • 0

#12 Delphius

Delphius

    Advanced Member

  • Administrador
  • 6.295 mensajes
  • LocationArgentina

Escrito 31 agosto 2011 - 09:54

Hola,
A mi entender, y advierto que con poca experiencia, sobre GUID lo mejor, rápido y sencillo es cortar por lo simple: almacenar eso como string.
O cuanto mucho, definir varios campos que almacenen cada grupo y trabajar con ellos en forma individual y "unirnos" en cuanto se requiera.


Sergio no me parece muy adecuada la idea de utilizar MD5... ten en cuenta que es un algoritmo irreversible. Y el punto es que se requiere poder recuperar el valor.


Ahora yo tengo una pregunta... ¿Y por casualidad Oracle no dispone de funciones, etc para operar con esto? Yo creeía que si.


Saludos,
  • 0

#13 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 01 septiembre 2011 - 09:50

Hola Delphius, tienes razón, Oracle tiene ya funciones para esto, muy fáciles de implementar en PL/SQL, pero tambien hay aplicaciones desconectadas que requieren que generen su Id y lo almacene localmente, para cuando se conecte al servidor informe los Id correspondientes y que estos no existan en la base de datos.
  • 0

#14 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 02 septiembre 2011 - 02:36

El problema de fondo que yo veo es que un GUID pasado a numero es del estilo (según tu propio ejemplo, no he hecho el calculo) de "113403348216831948512915122317315125024940" que según mis cuentas tiene 42 digitos... un NUMERIC de FireBird tiene un maximo de 18 digitos según creo, asi que no veo como vas a almacenar el numero en FireBird sin usar un campo de texto, aparte de necesitar una libreria de numeros grandes para calcularlo en delphi.

Sin aclarar antes este punto no hay mucho que hacer.

  • 0

#15 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 10 septiembre 2011 - 03:29


Hola a todos, no había podido dar repuesta por tanto trabajo, pero les cuento:
un hexadecimal EBD0A0A2B9E5443387C068B6B72699C7 lo convierto a decimal 313451834834061278758346828331453159879 así


[DELPHI]{Convierte un hexadecimal en un TBCD}
Function HexToBCD(s : string) : TBcd;
var
  bb : Byte;
  cc : Char;
begin
  Result := 0;
  s := UpperCase(s);
  for bb := 1 to Length(s) do
  begin
  Result := Result * 16;
  cc := s[bb];
  case cc of
    '0'..'9': Result:= Result + ord(cc) - ord('0');
    'A'..'F': Result:= Result + ord(cc) - ord('A')+10;
  else raise EConvertError.Create('Se ha encontrado un caráctar no valido.');
  end;
  end;
end;[/DELPHI]


y desde el hexadecimal lo paso a binario 11101011110100001010000010100010101110011110010101000100001100111000011111000000011010001011011010110111001001101001100111000111 así


[DELPHI] {Convierte un hexadecimal en un binario}
Function HexToBin(Hexadecimal:string):string;
const
      BCD: array [0..15] of string=
        ('0000','0001','0010','0011','0100','0101','0110','0111',
        '1000','1001','1010','1011','1100','1101','1110','1111');
var
    i:integer;
begin
    for i:=Length(Hexadecimal) downto 1 do
      Result:=BCD[StrToInt('$'+Hexadecimal)]+Result;
end;[/DELPHI]


desde el binario lo puedo pasar al hexadecimal así


[DELPHI]{Convierte un binario a hexadecimal}
function BinToHex(Binario:string):string;
const
      BCD: array [0..15] of string=
        ('0000','0001','0010','0011','0100','0101','0110','0111',
        '1000','1001','1010','1011','1100','1101','1110','1111');
      HEX: array [0..15] of char=
        ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F');
var
    i,n:integer;
    sTemp:string;
    sNibble:string;
begin
    Result:='';
    sTemp:=Binario+Copy('000',1,Length(Binario) mod 4);
    for i:=0 to (Length(Binario) shr 2)-1 do
    begin
    sNibble:=Copy(sTemp,(i shl 2)+1,4);
    n:=8;
    while (sNibble <> BCD[n]) do
      if sNibble < BCD[n] then Dec(n) else Inc(n);
    Result:=Result+HEX[n];
    end;
end;
[/DELPHI]
El gran problema es que desde el décimal no he podido pasarlo a hexadecimal, alguna idea?

  • 0

#16 Sergio

Sergio

    Advanced Member

  • Moderadores
  • PipPipPip
  • 1.092 mensajes
  • LocationMurcia, España

Escrito 12 septiembre 2011 - 02:48

Hacer esa conversion usando cadenas de texto es bastante complicado, muy complicado, porque al final necesitas usar el decimal como entero para poder hacer esto:



delphi
  1. function Dec2Hex(decimal: LongInt): string;
  2. const
  3.   Hex: array [0..15] of string = ('0', '1', '2','3','4','5','6','7','8','9','A','B','C','D','E','F');
  4. begin
  5.   result:= '';
  6.   while decimal>0 do begin
  7.     Digito:= decimal mod 16;
  8.     decimal:= decimal div 16;
  9.     result:= result + Hex[Digito];
  10.   end;
  11. end;



Esta es la base matematica, hacer divisiones por 16 y quedarse con el resto para saber el proximo digito en hexadecimal, y la division entera para continuar extrayendo digitos... pero si "decimal" es una cadena, el reproducir esto sin usar enteros muy largos es mucho mas dificil que en las otras conversiones que ya tienes... pero bueno, se podria hacer, necesitarias una funcion que dado un decimal en string te devuelva el resto de dividir por 16 y el resultado entero de esa division, y llamarlo dentro del while de arriba.

Lo que pasa es que muy tedioso: Primero, has de ir restandole 16 al decimal -que es un string- hasta que te quede algo menor de 16, ese algo es el resto, el proximo digito hexadecimal, mas luego has de llevar la cuenta de cuantas veces le restas 16, porque ese sera el numevo "decimal" para el siguiente digito.

Claro, visto asi no aparenta excesivamente dificil, pero ojo, el nuevo "decimal", esa cuenta de veces que le restamos 16, no te va a caber en un LongInt, asi que has de llevar esa cuenta de nuevo en un string, y sumarle 1 a un entero en un string pues tambien tienes que programarlo, asi que necesitas dos rutinas nuevas:



delphi
  1. procedure Div16(entero: string; var resto: integer; var resultado: string);
  2. begin
  3.   resultado:= '0';
  4.   //Detecto cuando he terminado de restar 16....
  5.   //Como el segundo 'or' no se calcula si el primero da true, no desbordare el StrToInt()
  6.   while (Length(entero)>2) or (StrToInt(entero)>15) do begin
  7.     entero:= Quitar16(entero);
  8.     resultado:= Sumar1(resultado);
  9.   end;
  10.   resto:= StrToInt(entero);
  11. end;



Y ahora te faltarian las otras dos: Quitar16(numero: string): string; y Sumar1(numero: string): string;

Una vez que te he soltado todo este rollo, mi consejo es que para esto uses rutinas de precision arbitraria, la que te comente al principio, lo que consigues es poder usar el while primero sin tener que acudir hacer calculos con numeros dentro de strings, que es un verdadero follón, lento y delicado de hacer... y ya está hecho en estas rutinas!

Por cierto, estas rutinas hacen lo mismo pero con arrays dinamicos de digitos y cosas asi, no es tan diferente a lo que te cuento, pero se busca eficiencia (no quedarse sin memoria) y velocidad (poder hacer cosas "serias" con precisión arbitraria).
  • 0

#17 mightydragon_lord

mightydragon_lord

    Advanced Member

  • Miembros
  • PipPipPip
  • 73 mensajes

Escrito 14 septiembre 2011 - 07:35

Muchas gracias por sus respuestas, me han ayudado muchísimo, les dejo la función que finalmente me sirvió para solucionar mi problema.

[/color][/size]

delphi
  1. [font=monospace][size=13px][color=gray][color=rgb(0, 0, 0)][font=Verdana][/font][/color][/size][/font]
  2.  
  3.  
  4. {Convierte un TBCD en un hexadecimal}
  5. function BCDToHex(BCD: TBCD): string;
  6. const
  7.   B16: array[0..15] of Char = ('0','1','2','3','4','5','6','7','8','9',
  8.         'A','B','C','D','E','F');
  9. var
  10.   BcdEx,DivBcd: TBCD;
  11.   ModInt,SepDec: Integer;
  12. begin
  13.   Result:= '';
  14.   while (Bcd > 0) do
  15.   begin
  16.   BCDDivide(Bcd,16,BcdEx);
  17.   SepDec:=Pos(DecimalSeparator,BCDToStr(BcdEx))-1;
  18.   If SepDec > 0 then begin
  19.     DivBcd:=StrToBCD(Copy(BCDToStr(BcdEx),0,SepDec));
  20.   end else begin
  21.     DivBcd:=BcdEx;
  22.   End;
  23.   ModInt:=StrToInt(BCDToStr(Bcd - (16 * DivBcd)));
  24.   Result:= B16[ModInt] + Result;
  25.   BCD:=DivBCD;
  26.   end;
  27. end;


también les dejo un proyecto de ejemplo, gracias nueva mente =)

Archivos adjuntos


  • 0




IP.Board spam blocked by CleanTalk.