2009-07-08 10 views
10

Tengo un código C que almacena cadenas ASCII en la memoria como una longitud de cuatro bytes seguida de la cadena. Las longitudes de cadena están en el rango de 10-250 bytes.Compresión de cadenas ASCII en C

Para reducir la ocupación me gustaría comprimir cada cuerda individualmente sobre la marcha, aún almacenando la longitud (de la cuerda comprimida) seguido de la cuerda comprimida.

No quiero comprimir en un ámbito mayor que las cadenas individuales porque cualquier cadena se puede leer/escribir en cualquier momento.

¿Qué bibliotecas/algoritmos están disponibles para hacer esto?

Gracias por su ayuda. NickB

Respuesta

14

ZLib está siempre a su servicio; tiene una sobrecarga muy pequeña para los casos cuando la cadena contiene datos no comprimibles, es relativamente rápida, gratuita y se puede integrar fácilmente en programas C y C++.

3

Zlib es definitivamente su amigo aquí, pero asegúrese de realizar algunas pruebas para detectar la longitud de cadena promedio en la que la compresión comienza a ser beneficiosa, debido a la pequeña sobrecarga de los encabezados de compresión.

Por ejemplo, puede descubrir que con menos de 20 caracteres, la cadena comprimida es realmente más grande y, por lo tanto, solo comprime las cadenas más largas.

+0

Y si puede reservar 1 bit del campo de tamaño para marcar si la cadena está comprimida o no, ni siquiera tiene que adivinar: solo intente comprimir cada cadena. Si se vuelve más pequeño, guárdelo comprimido. Si no es así, guárdelo sin comprimir. Esto es más o menos lo que permite PKZIP (y supongo que otros contenedores comprimidos, es solo PKZIP es el que he implementado una vez). Desafortunadamente, el rango de tamaño 10-250 no admite de manera eficiente un bit "repuesto" en una arquitectura de 8 bits. –

3

¿Por qué utilizar una longitud de 4 bytes cuando las cadenas tienen una longitud de 10-250 bytes? Utilice una longitud de 1 byte que le ahorrará 3 bytes por cadena solo.

¿Los datos son solo textuales, es decir, 0-9 A-z o algún subconjunto? Si es así, vuelva a codificarlo para usar ese subconjunto y guardar algunos bits por carácter.

Ahora eche un vistazo a http://gnosis.cx/publish/programming/compression_primer.html en la sección de codificación de Huffman y sección de lempel-zev.

Eso debería comenzar.

4

No estoy seguro de que los enfoques de compresión zlib o LZW funcionen bien en el caso de comprimir individualmente cadenas cortas de menos de 250 bytes. Ambos requieren típicamente crear un diccionario bastante considerable antes de que se vean ganancias de compresión significativas.

¿Quizás la codificación simple de Huffman con un árbol de codificación fijo, o una compartida entre todas las instancias de las cadenas? Además, ¿ha visto la codificación ZSCII utilizada para comprimir cadenas cortas en microcomputadoras con memoria limitada en los años 80?

link text

10

La mayoría de los algoritmos de compresión no funcionan muy bien con cadenas cortas. Aquí hay algunos algoritmos de compresión que están diseñados para comprimir cadenas cortas de texto en inglés. Si bien pueden manejar cualquier byte arbitrario en la cadena de texto sin formato, tales bytes a menudo hacen que los datos "comprimidos" sean más largos que el texto sin formato. Por lo tanto, es una buena idea que el compresor almacene datos "no comprimibles" sin cambios y establezca un indicador "literal" en dichos datos (como sugirió Steve Jessop).

  • "base 40 de codificación": máxima compresión 3: 2
  • "Código Estándar Zork para Intercambio de Información" (ZSCII): máximo de compresión 3: 2
  • byte pair compression: máxima compresión 2: 1
  • una tabla estática de Huffman compartida entre todas las cadenas (como sugiere cygil).
    • idealmente, formado a partir de las frecuencias de caracteres exactas de todos sus datos reales.
    • Varicode: máxima compresión 2: 1
  • PalmDoc compression (compresión par byte + una simple variante de LZ77).
1

Cuando se utilizan varias cadenas como esta es posible evitar la sobrecarga puntero para cada cadena (4 u 8 bytes cada uno) mediante la concatenación de ellos junto con \0 s (1 byte) y el uso de una función de búsqueda.

#include <stdio.h> 

static const char strings[]="hello\0world\0test"; 

char * nthstring(const char *s, unsigned n){ 
    while(n--) 
     while(*s++) 
     ; 
    return s; 
} 
int main(void) { 
    printf("%s\n",nthstring(strings,1)); 
    return 0; 
} 

Sin embargo, si la longitud de la cadena es menor que UCHAR_MAX puede optimizar las operaciones de búsqueda mediante el uso de los marcadores de posición cero bytes para almacenar longitudes (más 1 adicional al principio) Esto cuesta sólo 1 byte de datos adicional, pero ahorra mucho de saltos condicionales e incrementos en la función de búsqueda.

#include <stdio.h> 
/* each "string" is prefixed with its octal length */ 
static const char lenstrings[]="\05hello\05world\04test"; 

char * ithstring(const char *s, unsigned n){ 
    while(n--){ 
     s+=*s+1; 
    } 
    return s; 
} 
int main(void) { 
    char *s=ithstring(lenstrings,1); 
    /* use the length because we don't have terminating \0 */ 
    printf ("%.*s",(unsigned char)*s,s+1); 
    //write(1,s+1,(unsigned char)*s); //POSIX variation via <unistd.h> 
    return 0; 
} 

Para ambas variaciones, es mejor mantener primero las cadenas más necesarias; sin embargo, el segundo método le permitirá usar datos comprimidos (elija el que mejor funcione para sus datos - David Cary's answer tiene una lista de soluciones viables) siempre que ajuste los separadores de longitud a la longitud comprimida.

Nota: Para conseguir la máxima compresión de los compresores estándar, es probable que desee modificar el campo de longitud de sus cabeceras ser unsigned char (o unsigned short si las longitudes de serie más de 256, pero no 65536 bytes) ya que la mayoría de ellos se trate para admitir la compresión de archivos de gran tamaño (esto podría ahorrar 3-7 bytes por cadena)