¿Qué tipo de codificación puedo usar para acortar una cadena?

13

Estoy interesado en codificar una cadena que tengo y tengo curiosidad por saber si hay un tipo de codificación que pueda usarse que solo incluya caracteres alfabéticos y numéricos y preferiblemente acorte la cantidad de caracteres necesarios para representar la cadena.

Hasta ahora he analizado el uso de la codificación Base64 para hacer esto, pero parece que hace que mi cadena sea más larga y, a veces, incluye lo ==que me gustaría evitar. Ejemplo:

nombre de prueba | 120101

se convierte

dGVzdCBuYW1lfDEyMDEwMQ ==

que va de 16 a 24 caracteres e incluye caracteres no alfanuméricos.

¿Alguien sabe de un tipo diferente de codificación que pueda usar para cumplir mis requisitos? Puntos de bonificación si está integrado en el marco .NET o existe una biblioteca de terceros que hará la codificación.

Abe Miessler
fuente
1
¡No puedo usar una pérdida menos compresión como la codificación de Huffman! Son ideales para mensajes de texto ... pero al recibirlo, realmente debería saber sobre esta mutación que ha hecho para recuperar el texto.
66
Estás describiendo compresión, no codificación
Andy Smith
@ Andrew - Ok, ¿alguna sugerencia?
Abe Miessler

Respuestas:

30

El '=' o '==' final en Base64 solo está ahí para hacer que el número de caracteres sea múltiplo de 4. Puede eliminarlo, ya que siempre puede volver a colocarlo más adelante. Tenga en cuenta que Base64 se llama así porque usa 64 caracteres distintos. Letras mayúsculas, minúsculas y dígitos, eso es 62. Entonces Base64 también usa '/' y '+', que pueden o no ajustarse a su factura.

En general, si desea codificar secuencias arbitrarias de bytes en caracteres alfanuméricos, necesariamente hay alguna extensión de longitud en alguna parte, porque hay 256 valores posibles para un byte y solo 62 caracteres alfanuméricos. A veces se le llama el principio del casillero . Un esquema de codificación debe tener una extensión de longitud promedio de un factor log 256 / log 62 = 1.344 (promedio sobre todas las secuencias de bytes); de lo contrario, significa que algunas palomas están siendo aplastadas hasta la muerte en algún lugar y no las recuperará sin daños (lo que significa: dos cadenas distintas codificadas en el mismo, por lo que la decodificación no puede funcionar de manera confiable).

Ahora, es muy posible que sus cadenas no sean exactamente "secuencias de bytes uniformemente aleatorios"; sus cadenas tienen algún significado, lo que significa que no se producirá la secuencia de bytes más posible, porque no tienen sentido. Sobre esa base, probablemente pueda diseñar un esquema de codificación que incurrirá en una extensión de longitud menor que la Base64 genérica (o Base62 si necesita apegarse a caracteres alfanuméricos estrictos). Esta es una compresión de datos sin pérdidas . Funciona sobre un modelo probabilístico claramente definido de lo que puede aparecer como entrada.

Resumen: no puede existir un esquema genérico para codificar cadenas en secuencias alfanuméricas de modo que no se produzca una extensión de longitud pequeña o pequeña; Es una imposibilidad matemática. Probablemente pueda existir un esquema específico diseñado para el tipo de cadena de entrada que espera (pero dado que no le dice qué tipo de cadena puede encontrar, nadie puede ayudarlo en esto).

Tom Leek
fuente
1
+1, excelente explicación. No sabía que el =/ ==estar relacionado con la longitud tenía que ser un múltiplo de 4. Es posible que pueda
solucionar
Eso sí, esto supone una falta de casilleros. Unicode tiene muchas letras. Realmente necesitamos una mejor comprensión del problema real .
MSalters
@ Tom, ¿cómo calculó el factor de extensión de longitud promedio usando la división de registros? Basado en el diagrama en en.wikipedia.org/wiki/Base64 , tiene un sentido intuitivo que para cada carácter no codificado se necesitan 4/3 caracteres en Base64 para representar. Solo me preguntaba cómo llegaste a la misma conclusión con las matemáticas ... gracias :)
Jonathan Lin
Mi mala pregunta estúpida. log (256) = 8 bits, log (64) = 6 bits, por lo tanto, la relación es 8/6 = 4/3 = 1.333 para Base64. Salud.
Jonathan Lin
4

La codificación de caracteres generalmente se realiza cuando el sistema receptor no puede procesarlos. Por ejemplo, BASE64 representa datos usando 6 bits (2 6 , por lo tanto 64) de caracteres para representar secuencias de datos más largas (el "==" que aparece a veces al final es un relleno para la alineación). Esto se debe a que su archivo de imagen en el correo electrónico puede tener 0xFE y su servidor de correo no estará contento de transmitir eso (o cualquier otro carácter tradicionalmente no impreso).

No hay codificación que "reduzca el tamaño". Las codificaciones son solo asignaciones de bits al personaje que representan. Dicho esto, ASCII es un juego de caracteres de 7 bits (codificación) que a menudo se almacena en 8 bits de espacio. Si limita los rangos que acepta, también puede eliminar los caracteres de control.

El uso de este método significa que tiene que escribir las cosas a nivel de bits, y también juega un poco infierno con la velocidad y las instrucciones de la máquina porque todas las máquinas modernas tienen alineaciones que son múltiplos de 8 bits. Por eso, por ejemplo, Unicode es UTF-8, UTF-16 y UTF-32.

Si está haciendo esto por seguridad (es por eso que lo publicó en Security.SE, ¿verdad?), Simplemente filtre las cosas y guárdelas normalmente. Si está haciendo esto para ahorrar espacio, considere si todo el código adicional y el tiempo de acceso más lento (porque la mayoría de las entradas cruzarán los límites de la dirección) vale la pena el ahorro de espacio.

Por cierto, el siguiente es un fragmento de un curso de CS donde tuvimos que convertir ASCII de almacenamiento de 8 bits a 7 bits:

    memset(dest,0x00,8);
    memcpy(dest, source, length);

    for (int i = 0; i < 8; i++) {
            if (dest[i] & 0x80) {
                    fprintf(stderr, "%s: %s\n", dest, "Illegal byte sequence");
                    exit(EILSEQ);
            }
    }

    dest[0] = 0x7F & dest[0] | 0x80 & dest[1] << 7;
    dest[1] = 0x3F & dest[1] >> 1 | 0xC0 & dest[2] << 6;
    dest[2] = 0x1F & dest[2] >> 2 | 0xE0 & dest[3] << 5;
    dest[3] = 0x0F & dest[3] >> 3 | 0xF0 & dest[4] << 4;
    dest[4] = 0x07 & dest[4] >> 4 | 0xF8 & dest[5] << 3;
    dest[5] = 0x03 & dest[5] >> 5 | 0xFC & dest[6] << 2;
    dest[6] = 0x01 & dest[6] >> 6 | 0xFE & dest[7] << 1;
    dest[7] = 0x00; //Clearing out
Jeff Ferland
fuente
2

Puede comprimir los datos con, por ejemplo, gzip, bzip2 o lzma y luego ejecutar a través de base64 para limitar el conjunto de caracteres utilizado. Esto es beneficioso solo en cadenas más grandes de cientos de bytes o más.

Antti Rytsölä
fuente
1

¿Por qué no usar la compresión LZ? Esta puede ser una forma decente de comprimir una cadena, pero sería más eficiente en el caso de cadenas largas. ¿Cuánto dura la cadena de destino que desea codificar?

A.Rashad
fuente
¿Cómo se compara la compresión LZ con gzip o bzip2 mencionados en la sugerencia de attir?
NoChance
gzip se basa en LZ y Huffman Coding. más en LZ en.wikipedia.org/wiki/LZ77
A.Rashad