Преобразовать пробелы в закладки - PullRequest
0 голосов
/ 22 декабря 2010

Я недавно решил изучать C, поэтому я начал изучать K & R, но я застрял в проблеме 21 в главе 1. Предполагается, что вы пишете программу, которая дает строку без вкладок и определенной ширины табуляции, преобразует все белоепробел в эквивалентном интервале, используя табуляцию и пробел.

Пока у меня есть это:

void entab (char from[], char to[], int length, int tabwidth)
{
    int i, j, tabpos, flag, count;

    j = tabpos = flag = count = 0;
    for (i = 0; from[i] != '\0' && j < length - count - 2; i++) {
        if (from[i] == ' ') {
            // If you see a space, set flag to true and increment the
            // whitespace counter. Don't add any characters until you reach the
            // next tabstop.
            count++;
            tabpos = (tabpos + 1) % tabwidth;
            flag = 1;
            if (count >= tabwidth - tabpos) {
                to[j] = '\t';
                j++;
                count = count - tabwidth + tabpos;
                tabpos = 0;
            }
        } else {
            if (flag == 1) {
                // if you see something other than a space and flag is true,
                // there weren't enough spaces to reach a tabstop. Add count
                // spaces to the string.
                flag = 0;
                tabpos = (tabpos + count + 1) % tabwidth;
                while (count > 0) {
                    to[j] = ' ';
                    j++;
                    count--;
                }
            } else {
                tabpos = (tabpos + 1) % tabwidth;
            }
            count = 0;
            to[j] = from[i];
            j++;
        }
    }
    to[j] = '\0';
    return;
}

, что, к сожалению, кажется, дает немного больший интервал, чем предполагалось,Любые идеи о том, где я облажался?

PS Я смотрел на другие решения онлайн, и я понимаю, что есть гораздо лучший подход к проблеме, но я действительно хотел бы исправить ошибку в моей.

РЕДАКТИРОВАТЬ: Задав ширину табуляции = 4 и используя:

    foobar
        foo  bar      foo bar
    foo     bar

в качестве ввода, я получаю:

/t/tfoobar
/t/t/t/tfoo  bar/t/t/t foo bar
/t/tfoo/t/t bar

в качестве вывода, тогда как правильный вывод будет:

/tfoobar
/t/tfoo/t bar/t/t foo bar
/tfoo/t/tbar

Ответы [ 3 ]

4 голосов
/ 22 декабря 2010

Столько кода в одной функции и с таким большим количеством вложений (четыре уровня глубиной) трудно понять правильно и еще сложнее поддерживать.

Я бы предложил вам сначала рефакторинг того, что у вас есть, на более управляемые части. Например:

  • создайте функцию findTab, которая принимает строку для тестирования, ширину вкладки (количество пробелов) и возвращает индекс первого появления вкладки.
  • Затем создайте функцию, называемую чем-то вроде replaceChars, которая получает право на аргументы и выполняет замену

Это всего лишь пара идей. В итоге вы получите несколько более коротких и гораздо более управляемых функций, и, скорее всего, они найдут ваши ошибки на своем пути!

Кстати, я попытался быстро найти в Google некоторые статьи по рефакторингу на Си, но увы ушел с пустыми руками. В наши дни рефакторинг - это все о языках ООП, таких как Java, C # и C ++. Тем не менее, несколько правил будут иметь большое значение для вас:

  • Минимизация вложенности блоков кода: if операторов и for & while циклов
  • Минимизируйте количество строк на функцию (в пределах разумного), оставьте его менее 60 вершин
  • (заимствовано из классов ООП), чтобы каждая функция очень хорошо выполняла одну очень ограниченную вещь
  • Назовите ваши переменные и методы более многословно - это кажется болью, когда вы делаете это, но это принесет дивиденды в будущем, как сейчас.

Удачи и добро пожаловать в ТАК!

2 голосов
/ 22 декабря 2010
if (count >= tabwidth - tabpos)

Это начинает выводить вкладки слишком рано.Рассмотрим входную строку:

"aa              " 

с 8 в качестве табуляции.Вы попадаете на символ 5 (мне будет 4, количество будет 3), когда tabpos становится также 5, что вызывает условие.Вы не хотите выводить вкладки до того, как ваша переменная i достигнет значения tab tab.

Я не исправлю ваш код.Но вместо того, чтобы делать общие утверждения, такие как «рефакторинг вашего кода», что является просто способом 21-го века сказать «переписать ваш код», я могу указать на одну очевидную ошибку.Вам не нужна переменная tabpos.Просто используйте (я% Tabwidth).С этого момента вещи должны начать становиться на свои места.

0 голосов
/ 22 декабря 2010

Одна вещь, которая крайне подозрительна в вашем коде, - это способ увеличения количества табов.Вы увеличиваете его (mod tabwidth) для каждого пробела дважды .Один раз после увеличения count и еще раз при flag == 1.

При отладке, особенно кода, имеющего условия, которые влияют на будущие итерации цикла, полезно подумать об инвариантах.Это операторы, которые всегда должны быть истинными, когда вы выполняете определенную строку кода.

Другое дело: присвойте своим переменным лучшие имена.flag и count особенно плохи, но tabpos также немного сбивает с толку.Моей первой мыслью было "положение какой вкладки?"но похоже, что вы на самом деле хотите, чтобы это было количество ячеек символов с момента последней табуляции (или, что эквивалентно, вашей текущей позиции в столбце табуляции).

В любом случае они должны иметь более четкие значения.имена.Если концепцию слишком сложно назвать хорошо, то это может быть признаком того, что вам необходимо переосмыслить свой алгоритм, или вы должны хотя бы прокомментировать переменные.Как правило, неплохо бы также определить инварианты для переменных, например, «tabpos - это число символьных ячеек с момента последней табуляции» (что нарушает ваш код == ошибка).

Наконец, вы можете попробовать пошаговочерез ваш код в отладчике или даже с помощью отладки printf, чтобы понять, почему он плохо себя ведет на некоторых входах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...