Мне кажется, у вас смешались две проблемы.
Первый, как указал @Darren, называется Кодирование длины выполнения. : найдите последовательность идентичных байтов и замените их одним байтом, за которым следует количество повторений. Второй, насколько я могу судить, заключается в том, чтобы подсчитать, сколько некоторых «специальных» символов встречается в строке.
Полное кодирование
Я собираюсь дать реализацию RLE, отличную от реализации @Darren. Как и его решение, мое не касается частей = «специального символа» задания. я собираюсь начать с
void
rll_encode(char *in, char *out)
{
while (*in != '\0') {
int len = find_run(in);
out = emit_run(out, *in, len);
in = in + len; // advance the input
}
*out = '\0';
}
Это скелет кодирования длин серий: перемещаться по входным находкам, а затем выдавать их на выходе, соответствующим образом закодированные. Этот цикл состоит из трех шагов:
- Функция
find_run
будет искать самый длинный разрешенный запуск, начиная с текущего места во входных данных, на которое указывает in
. Он возвращает длину этого прогона, которая всегда будет больше нуля.
- Точно так же
emit_run
принимает символ и количество повторений и генерирует правильную кодировку в выходной буфер. Он возвращает следующее местоположение для использования в выходном буфере.
- После запуска мы продвигаем указатель во входной буфер на
len
байт и повторяем цикл.
После завершения цикла мы добавляем байт NUL в выходной буфер, чтобы он сформировал допустимую строку. В реальном компрессоре любого типа этот последний шаг не будет выполняться, и входной и выходной буферы будут иметь связанные с ними размеры.
Осталось только реализовать find_run
и emit_run
. Начнем с emit_run
, так как это немного проще:
char *
emit_run(char *out, char c, int len)
{
*out++ = c;
*out++ = '0' + len;
return out;
}
Для этого требуется выходной буфер out
, символ c
и связанный с ним счетчик повторов len
. Учитывая, например, c == 'A'
и len == 5
, он добавляет C5
к выходному буферу.
С этой функцией есть одна довольно серьезная проблема. Рассмотрим, что происходит со строкой "ABCDE"
: каждая буква имеет число повторений, равное единице, поэтому строка кодируется как "A1B1C1D1E1"
, что вряд ли сильно сжато. Существует несколько подходов к этой проблеме, некоторые из которых обсуждаются в ответах на этот вопрос, и все это можно реализовать небольшими изменениями в emit_run
.
Это оставляет нас с проблемой поиска прогонов в первую очередь.
int
find_run(char *in)
{
char run_char = *in;
int run_len = 0;
for (;;) {
char c = *in;
bool run_ended =
c != *run_char || // catches '\0', too
run_len == MAX_RUN;
if (run_ended)
break;
run_len++;
in++;
}
return run_len;
}
Эта функция получает место для начала сканирования, in
, и возвращает, сколько раз повторяется первый символ ввода.
- Скопируйте первый символ буфера в
run_char
и инициализируйте run_len
нулем.
- Посмотрите на каждый символ
c
во входных данных и решите, закончился ли запуск или нет. Прогон заканчивается, если либо c
не равно run_char
, либо если прогон достиг своей максимальной длины. Обратите внимание, что проверка на то, что c
не равно run_char
, также обрабатывает попадание в конец строки, т. е. c
равно NUL
.
- Если прогон закончился, выйти из цикла и вернуть длину прогона.
- Если прогон не закончился, переместитесь вперед во вводе на один символ и увеличьте длину прогона.
Все эти части вместе реализуют простую версию кодирования длин серий. Ниже приведен скелет небольшой программы для ее проверки.
#include <stdio.h>
#include <stdbool.h>
#define MAX_RUN 9
/* insert emit_run from above */
/* insert find_run from above */
/* insert rll_encode from above */
int main(int argc, char **argv)
{
char out[1024];
rll_encode(argv[1], out);
printf("%s\n", out);
}
Я попытался настроить эту конкретную реализацию, чтобы максимизировать ясность алгоритма, но версия @Darren ближе к тому, что вы увидите в производственном коде, поскольку вся реализация находится в одной функции. Его выбор кодирования на месте, безусловно, верен, хотя я думаю, что распространены как версии на месте, так и версии с отдельным выходным буфером. О первых немного сложнее рассуждать, если вы новичок в C, и особенно в указателях. Кроме того, в любых производственных версиях и входной, и выходной буферы будут заданы с явной длиной, и будет дополнительный код для проверки переполнения выходного буфера, оба из которых я проигнорировал здесь.
Подсчет символов
Что касается подсчета символов, не пытайтесь сохранить магическую переменную для каждого отдельного специального символа. Вместо этого я бы предложил использовать массив из 256 элементов для накопления количества всех символов, а затем распечатать только нужные записи.
Это довольно простая модификация find_run
, если вы используете глобальный массив, хотя, опять же, вы бы не сделали этого в реальной реализации.
person
Dale Hagglund
schedule
03.11.2013