2010-03-11 15 views
8

Estoy tratando de escribir una función que convierta una matriz grande arbitraria de bytes (mayores de 64 bits) en un número decimal representado como una cadena en C# y simplemente puedo No descubra cómo hacerlo.Convertir una matriz de bytes en un número decimal como una cadena

Por ejemplo el siguiente código ...

Console.WriteLine(ConvertToString(
    new byte[] 
    { 
    0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 
    0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00 
    })); 

.. debe imprimir

22774453838368691933757882222884355840 

No quiero que sólo tiene que utilizar una biblioteca extra como biginteger para eso, porque quiero es simple y le gusta entender cómo funciona.

+2

que se pegaba a la utilización de la biblioteca BigInteger, y simplemente utilizar el reflector para ver cómo funciona. Reinventar este código es muy innecesario. – Codesleuth

+0

Obviamente, él quiere aprender el método detrás de esto. – Skurmedel

Respuesta

2

@ Sobre la base de la respuesta de Wilheim:

static string BytesToString(byte[] data) { 
    // Minimum length 1. 
    if (data.Length == 0) return "0"; 

    // length <= digits.Length. 
    var digits = new byte[(data.Length * 0x00026882/* (int)(Math.Log(2, 10) * 0x80000) */ + 0xFFFF) >> 16]; 
    int length = 1; 

    // For each byte: 
    for (int j = 0; j != data.Length; ++j) { 
     // digits = digits * 256 + data[j]. 
     int i, carry = data[j]; 
     for (i = 0; i < length || carry != 0; ++i) { 
      int value = digits[i] * 256 + carry; 
      carry = Math.DivRem(value, 10, out value); 
      digits[i] = (byte)value; 
     } 
     // digits got longer. 
     if (i > length) length = i; 
    } 

    // Return string. 
    var result = new StringBuilder(length); 
    while (0 != length) result.Append((char)('0' + digits[--length])); 
    return result.ToString(); 
} 
0

¿Satisfacería System.Decimal a sus necesidades?

+0

No, MSDN dice: "El tipo de valor decimal representa números decimales que van desde positivos 79,228,162,514,264,337,593,543,950,335 a negativos 79,228,162,514,264,337,593,543,950,335". – Martin

5

Algunas pautas:

  1. Tendrá que guardar los dígitos del número en una matriz, un espacio para cada dígito. La matriz comienza vacía
  2. Luego necesita una matriz para guardar la multiplicación de la matriz. También comienza vacío.
  3. Ahora para cada byte tiene:
    1. Primera multiplicar cada dígito de la matriz número actual por 256, salvo el 10 de módulo en el dígito temporal correspondiente y añadir el número dividido por 10 a la siguiente multiplicación dígitos.
    2. Asigna la matriz de multiplicación temporal a la matriz de dígitos actual.
    3. A continuación, agregue el byte al primer dígito.
    4. Corrija la matriz actual, guardando solo los 10 módulos en cada índice y pasando el valor dividido por 10 al índice siguiente.
  4. Luego debe concatenar cada dígito de una cadena.
  5. Devuelve la cadena.

No se olvide de ampliar cada matriz según sea necesario o determinar el tamaño máximo necesario a partir del número de bytes pasados.

Edición, ejemplo después de la tercera etapa anterior:

Valores = [0xAA, 0xBB] inicial actual = [] Temperatura inicial = []

Con 0xAA

  1. Nada que multiplicar.
  2. Sin cambios en la asignación.
  3. Añadimos 0xAA al primer valor de la corriente: Corriente = [170]
  4. Corregimos actual para guardar sólo el módulo, pasando el valor dividido por diez al siguiente valor:
    1. 1ª cifra: actual = [0] pase 17.
    2. 2do dígito: actual = [0, 7] pase 1.
    3. 3er dígito: Actual = [0, 7, 1] no hay valor que pasar para que el proceso finalice.

Ahora con 0xBB

  1. Multipli por 256, salvo en temp y correcta, hacer para cada dígito:
    1. primera dígitos: Temp = [0], 0 para guardar al siguiente dígito.
    2. 2do dígito: Temp = [0, 1792] antes de la corrección, Temp = [0, 2], 179 para pasar después de la corrección.
    3. Tercer dígito: Temp = [0, 2, 1 * 256 + 179 = 435] antes de la corrección, Temp = [0, 2, 5], 43 para pasar después de la corrección.
    4. 4º dígito: Temp = [0, 2, 5, 43] antes, Temp = [0, 2, 5, 3], 3 pasar después de
    5. 5º dígito: Temp = [0, 2, 5, 3, 4] antes y después de la corrección, sin dígitos para guardar, por lo que la multiplicación termina.
  2. Temp actual a actual: Actual = [0, 2, 5, 3, 4]; Temp = []
  3. Añadir el valor actual para el primer dígito: Current = [187, 2, 5, 3, 4]
  4. corregir los valores:
    1. primera dígitos: Current = [7, 2 , 5, 3, 4], 18 pasar.
    2. 2do dígito: Actual = [7, 0, 5, 3, 4], 2 para aprobar.
    3. Tercer dígito: Actual = [7, 0, 7, 3, 4], nada que pasar, por lo que la adición finaliza.

Ahora sólo tenemos que concatenar para el resultado 43707.

+0

@Wilhelm: No creo que entiendo tu descripción (pero parece que podría funcionar) ... ¿puedes darme un ejemplo con dos bytes (por ejemplo, 0xAA y 0xBB), por favor? – Martin

+0

"matriz" tiene las connotaciones erróneas para mí, simplemente las llamaría matrices o listas. –

+0

Los dígitos máximos para 'n' bytes son' ceil (8 n log_10 (2)) '. Usando 16.16 punto fijo: '(n * 0x00026882 + 0xFFFF) >> 16'. –

1

¿Quieres entender el funcionamiento por lo que echar un vistazo a super cool C# BigInteger Class @ CodeProject.

Además, he despojado de esa clase a los elementos esenciales para esta pregunta. Se puede optimizar aún más. :)

¡Intenta copiar y pegar el código siguiente funciona!

using System; 

public class BigInteger 
{ 
    // maximum length of the BigInteger in uint (4 bytes) 
    // change this to suit the required level of precision. 

    private const int maxLength = 70; 


    private uint[] data = null;    // stores bytes from the Big Integer 
    public int dataLength;     // number of actual chars used 


    public BigInteger() 
    { 
     data = new uint[maxLength]; 
     dataLength = 1; 
    } 

    public BigInteger(long value) 
    { 
     data = new uint[maxLength]; 
     long tempVal = value; 

     dataLength = 0; 
     while (value != 0 && dataLength < maxLength) 
     { 
      data[dataLength] = (uint)(value & 0xFFFFFFFF); 
      value >>= 32; 
      dataLength++; 
     } 

     if (tempVal > 0)   // overflow check for +ve value 
     { 
      if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) 
       throw (new ArithmeticException("Positive overflow in constructor.")); 
     } 
     else if (tempVal < 0) // underflow check for -ve value 
     { 
      if (value != -1 || (data[dataLength - 1] & 0x80000000) == 0) 
       throw (new ArithmeticException("Negative underflow in constructor.")); 
     } 

     if (dataLength == 0) 
      dataLength = 1; 
    } 

    public BigInteger(ulong value) 
    { 
     data = new uint[maxLength]; 

     // copy bytes from ulong to BigInteger without any assumption of 
     // the length of the ulong datatype 

     dataLength = 0; 
     while (value != 0 && dataLength < maxLength) 
     { 
      data[dataLength] = (uint)(value & 0xFFFFFFFF); 
      value >>= 32; 
      dataLength++; 
     } 

     if (value != 0 || (data[maxLength - 1] & 0x80000000) != 0) 
      throw (new ArithmeticException("Positive overflow in constructor.")); 

     if (dataLength == 0) 
      dataLength = 1; 
    } 

    public BigInteger(BigInteger bi) 
    { 
     data = new uint[maxLength]; 

     dataLength = bi.dataLength; 

     for (int i = 0; i < dataLength; i++) 
      data[i] = bi.data[i]; 
    } 

    public BigInteger(byte[] inData) 
    { 
     dataLength = inData.Length >> 2; 

     int leftOver = inData.Length & 0x3; 
     if (leftOver != 0)   // length not multiples of 4 
      dataLength++; 


     if (dataLength > maxLength) 
      throw (new ArithmeticException("Byte overflow in constructor.")); 

     data = new uint[maxLength]; 

     for (int i = inData.Length - 1, j = 0; i >= 3; i -= 4, j++) 
     { 
      data[j] = (uint)((inData[i - 3] << 24) + (inData[i - 2] << 16) + 
          (inData[i - 1] << 8) + inData[i]); 
     } 

     if (leftOver == 1) 
      data[dataLength - 1] = (uint)inData[0]; 
     else if (leftOver == 2) 
      data[dataLength - 1] = (uint)((inData[0] << 8) + inData[1]); 
     else if (leftOver == 3) 
      data[dataLength - 1] = (uint)((inData[0] << 16) + (inData[1] << 8) + inData[2]); 


     while (dataLength > 1 && data[dataLength - 1] == 0) 
      dataLength--; 

     //Console.WriteLine("Len = " + dataLength); 
    } 

    public override string ToString() 
    { 
     return ToString(10); 
    } 

    public string ToString(int radix) 
    { 

     string charSet = "ABCDEF"; 
     string result = ""; 

     BigInteger a = this; 

     BigInteger quotient = new BigInteger(); 
     BigInteger remainder = new BigInteger(); 
     BigInteger biRadix = new BigInteger(radix); 

     if (a.dataLength == 1 && a.data[0] == 0) 
      result = "0"; 
     else 
     { 
      while (a.dataLength > 1 || (a.dataLength == 1 && a.data[0] != 0)) 
      { 
       singleByteDivide(a, biRadix, quotient, remainder); 

       if (remainder.data[0] < 10) 
        result = remainder.data[0] + result; 
       else 
        result = charSet[(int)remainder.data[0] - 10] + result; 

       a = quotient; 
      } 
     } 

     return result; 
    } 

    private static void singleByteDivide(BigInteger bi1, BigInteger bi2, 
             BigInteger outQuotient, BigInteger outRemainder) 
    { 
     uint[] result = new uint[maxLength]; 
     int resultPos = 0; 

     // copy dividend to reminder 
     for (int i = 0; i < maxLength; i++) 
      outRemainder.data[i] = bi1.data[i]; 
     outRemainder.dataLength = bi1.dataLength; 

     while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) 
      outRemainder.dataLength--; 

     ulong divisor = (ulong)bi2.data[0]; 
     int pos = outRemainder.dataLength - 1; 
     ulong dividend = (ulong)outRemainder.data[pos]; 

     if (dividend >= divisor) 
     { 
      ulong quotient = dividend/divisor; 
      result[resultPos++] = (uint)quotient; 

      outRemainder.data[pos] = (uint)(dividend % divisor); 
     } 
     pos--; 

     while (pos >= 0) 
     { 
      dividend = ((ulong)outRemainder.data[pos + 1] << 32) + (ulong)outRemainder.data[pos]; 
      ulong quotient = dividend/divisor; 
      result[resultPos++] = (uint)quotient; 

      outRemainder.data[pos + 1] = 0; 
      outRemainder.data[pos--] = (uint)(dividend % divisor); 
     } 

     outQuotient.dataLength = resultPos; 
     int j = 0; 
     for (int i = outQuotient.dataLength - 1; i >= 0; i--, j++) 
      outQuotient.data[j] = result[i]; 
     for (; j < maxLength; j++) 
      outQuotient.data[j] = 0; 

     while (outQuotient.dataLength > 1 && outQuotient.data[outQuotient.dataLength - 1] == 0) 
      outQuotient.dataLength--; 

     if (outQuotient.dataLength == 0) 
      outQuotient.dataLength = 1; 

     while (outRemainder.dataLength > 1 && outRemainder.data[outRemainder.dataLength - 1] == 0) 
      outRemainder.dataLength--; 
    } 



    public static void Main(string[] args) 
    { 

     BigInteger big = new BigInteger( new byte[] 
            { 
            0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 
            0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x00 
            }); 

     Console.WriteLine(big); 

    } 

} 
Cuestiones relacionadas