как изменить вход этой программы? (Язык C) - PullRequest
0 голосов
/ 11 декабря 2011

Как я могу заменить использование (FILE) и (fopen) на (scanf) для получения входных значений и отправки этих 2 функций?

Я хочу использовать эту функцию в коде Objective-c. Для получения дополнительной информации вы можете увидеть весь код здесь ссылка

static void stemfile(FILE * f)
{  while(TRUE)
{  int ch = getc(f);
    if (ch == EOF) return;
    if (LETTER(ch))
    {  int i = 0;
        while(TRUE)
        {  if (i == i_max) increase_s();

            ch = tolower(ch); /* forces lower case */

            s[i] = ch; i++;
            ch = getc(f);
            if (!LETTER(ch)) { ungetc(ch,f); break; }
        }
        s[stem(s,0,i-1)+1] = 0;
        /* the previous line calls the stemmer and uses its result to
         zero-terminate the string in s */
        printf("%s",s);
    }
    else putchar(ch);
}
}

int main(int argc, char * argv[])
{  int i;
    s = (char *) malloc(i_max+1);
    for (i = 1; i < argc; i++)
    {  FILE * f = fopen(argv[i],"r");
        if (f == 0) { fprintf(stderr,"File %s not found\n",argv[i]); exit(1); }
        stemfile(f);
    }
    free(s);
    return 0;
}

Ответы [ 2 ]

2 голосов
/ 11 декабря 2011

Функция scanf() не может быть прямой заменой существующего кода. Существующий код (который не очень хорошо написан для IMO) разбивает входной поток символов на буквы (определяемые макросом LETTER() как прописные или строчные буквы) и не буквы, и преобразует эти последовательности букв в строчные перед применением к ним функции stem().

Функция scanf(), с другой стороны, извлекает примитивные типы (int, char, double и т. Д.) И строки с явным разделением из входного потока. Разделители в данном коде (то есть все, что не является LETTER()) слишком расплывчаты для scanf() (хотя не для регулярного выражения). scanf() нужен определенный символ на каждом конце подстроки для поиска. Кроме того, scanf() не может автоматически преобразовываться в нижний регистр.

Предполагая, что ваши входные данные по-прежнему представляют собой файлы, я думаю, что самым простым решением может быть оставить код как есть и использовать его, каким бы сложным он ни был. В этом нет ничего такого, что не должно запускаться как часть большой программы Objective-C. В конце концов, Objective-C по-прежнему обеспечивает доступ к стандартной библиотеке C, по крайней мере, в пределах, установленных операционной системой (iOS гораздо более ограничивает, чем MacOS, если вы работаете на платформе Apple).

Общая проблема здесь заключается в токенизации: разбить входную последовательность неклассифицированных символов (например, символов) на последовательность классифицированных токенов (например, слов и пробелов). Распространенным подходом к проблеме является использование конечного автомата / автомата (FSA / FSM) для применения логики синтаксического анализа к входной последовательности и извлечения токенов по мере их появления. FSA может быть немного сложным в настройке, но он очень надежный и общий.

1 голос
/ 12 декабря 2011

Я все еще не уверен, почему вы хотите использовать scanf() в main(). Предположительно, это означало бы изменение интерфейса stemfile() (включая имя, поскольку он больше не будет обрабатывать файл), чтобы в качестве входных данных принималась строка символов. И scanf() усложнит жизнь; он будет читать строки, разделенные пробелами, которые могут быть частью его привлекательности, но он будет включать в себя любые знаки препинания, включенные в «слово».

Как заметил Рэндалл, код в существующей функции немного очевиден; Я думаю, что это может быть написано более просто следующим образом:

#include <stdio.h>
#include <ctype.h>
#define LETTER(x) isalpha(x)

extern int stem(char *s, int lo, int hi);

static void stemfile(FILE * f)
{
    int ch;
    while ((ch = getc(f)) != EOF)
    {
        if (LETTER(ch))
        {
            char s[1024];
            int i = 0;
            s[i++] = ch; 
            while ((ch = getc(f)) != EOF && LETTER(ch))
                s[i++] = ch;
            if (ch != EOF)
                ungetc(ch, f);
            s[i] = '\0';
            s[stem(s, 0, i-1)+1] = 0;
            /* the previous line calls the stemmer and uses its result to
               zero-terminate the string in s */
            printf("%s", s);
        }
        else
            putchar(ch);
    }
}

Я немного упростил вещи, превратив s в простую локальную переменную (похоже, она была глобальной, как и imax), удалив imax и функцию increase_s(). Они во многом связаны с функционированием функции.

Если вы хотите, чтобы вместо этого обрабатывалась строка с нулевым символом в конце, то:

static void stemstring(const char *src)
{
    char ch;
    while ((ch = *src++) != '\0')
    {
        if (LETTER(ch))
        {
            int i = 0;
            char s[1024];
            s[i++] = ch;
            while ((ch = *src++) != '\0' && LETTER(ch))
                s[i++] = ch;
            if (ch != '\0')
                src--;
            s[i-1] = '\0';
            s[stem(s,0,i-1)+1] = 0;
            /* the previous line calls the stemmer and uses its result to
               zero-terminate the string in s */
            printf("%s",s);
        }
        else
            putchar(ch);
    }
}

Это систематически меняет getc(f) на *src++, EOF на \0 и ungetc() на src--. Также (безопасно) изменяет тип ch с int (необходим для ввода / вывода) на char. Если вы беспокоитесь о переполнении буфера, вам придется немного поработать в функции, но на практике немногие слова будут даже 1024 байта (и вы можете использовать 4096 так же легко, как 1024, с соответственно меньшим - бесконечно малым - шансом реальных данных) переполнение буфера. Вам необходимо определить, является ли это «реальным» риском для вас.

Основной программой может стать довольно просто:

int main(void)
{
    char string[1024];
    while (scanf("%1023s", string) == 1)
        stemstring(string);
    return(0);
}

Очевидно, что из-за '1023' в формате это никогда не будет переполнять внутренний буфер. (Примечание: в первой версии этого ответа удалено . из "%.1023s"; scanf() отличается от printf()!).


Вызов: это работает?

Да - этот код ниже (добавление фиктивной stem() функции и незначительное изменение печати) работает для меня достаточно хорошо:

#include <stdio.h>
#include <ctype.h>
#include <assert.h>

#define LETTER(x) isalpha(x)
#define MAX(x, y) (((x) > (y)) ? (x) : (y))

static int stem(const char *s, int begin, int end)
{
    assert(s != 0);
    return MAX(end - begin - 3, 3);
}

static void stemstring(const char *src)
{
    char ch;
    while ((ch = *src++) != '\0')
    {
        if (LETTER(ch))
        {
            int i = 0;
            char s[1024];
            s[i++] = ch;
            while ((ch = *src++) != '\0' && LETTER(ch))
                s[i++] = ch;
            if (ch != '\0')
                src--;
            s[i-1] = '\0';
            s[stem(s,0,i-1)+1] = 0;
            /* the previous line calls the stemmer and uses its result to
               zero-terminate the string in s */
            printf("<<%s>>\n",s);
        }
        else
            putchar(ch);
    }
    putchar('\n');
}

int main(void)
{
    char string[1024];
    while (scanf("%1023s", string) == 1)
        stemstring(string);
    return(0);
}

Пример диалога

H:  assda23
C:  <<assd>>
C:  23
H:  3423///asdrrrf12312
C:  3423///<<asdr>>
C:  12312
H:  12//as//12
C:  12//<<a>>
C:  //12

Строки, помеченные H:, представляют собой вводимые человеком данные (H: не был частью ввода); строки, помеченные C:, являются выходными данными компьютера.


Следующая попытка

Проблема с концентрацией на гротескно длинных словах (1023 символа и более) состоит в том, что вы можете упустить простое. С scanf() чтением данных вы автоматически получаете одиночные «слова» без пробелов в качестве ввода. Вот отлаженная версия stemstring() с отладочным кодом печати на месте. Проблема была в двух ошибках. Один был в задании s[i-1] = '\0';, где -1 не был нужен. Другой был в обработке конца цепочки писем; функция while ((ch = *src++) != '\0') left src one place too far, which led to interesting effects with short words entered after long words (when the difference in length was 2 or more). There's a fairly detailed trace of the test case I devised, using words such as 'great' and 'book' which you diagnosed (correctly) as being mishandled. The stem () `здесь просто печатает свои входы и выходы и возвращает всю длину строки (так что не происходит никаких основ).

#include <stdio.h>
#include <ctype.h>
#include <assert.h>

#define LETTER(x) isalpha(x)
#define MAX(x, y) (((x) > (y)) ? (x) : (y))

static int stem(const char *s, int begin, int end)
{
    int len = end - begin + 1;
    assert(s != 0);
    printf("ST (%d,%d) <<%*.*s>> RV %d\n", begin, end, len, len, s, len);
//    return MAX(end - begin - 3, 3);
    return len;
}

static void stemstring(const char *src)
{
    char ch;
    printf("-->> stemstring: <<%s>>\n", src);
    while ((ch = *src++) != '\0')
    {
        if (ch != '\0')
            printf("LP <<%c%s>>\n", ch, src);
        if (LETTER(ch))
        {
            int i = 0;
            char s[1024];
            s[i++] = ch;
            while ((ch = *src++) != '\0' && LETTER(ch))
                s[i++] = ch;
            src--;
            s[i] = '\0';
            printf("RD (%d) <<%s>>\n", i, s);
            s[stem(s, 0, i-1)+1] = '\0';
            /* the previous line calls the stemmer and uses its result to
               zero-terminate the string in s */
            printf("RS <<%s>>\n", s);
        }
        else
            printf("NL <<%c>>\n", ch);
    }
    //putchar('\n');
    printf("<<-- stemstring\n");
}

int main(void)
{
    char string[1024];
    while (scanf("%1023s", string) == 1)
        stemstring(string);
    return(0);
}

Отображается вывод с отладкой (первая строка - типизированный ввод; остальные - вывод программы):

what a great book this is! What.hast.thou.done?
-->> stemstring: <<what>>
LP <<what>>
RD (4) <<what>>
ST (0,3) <<what>> RV 4
RS <<what>>
<<-- stemstring
-->> stemstring: <<a>>
LP <<a>>
RD (1) <<a>>
ST (0,0) <<a>> RV 1
RS <<a>>
<<-- stemstring
-->> stemstring: <<great>>
LP <<great>>
RD (5) <<great>>
ST (0,4) <<great>> RV 5
RS <<great>>
<<-- stemstring
-->> stemstring: <<book>>
LP <<book>>
RD (4) <<book>>
ST (0,3) <<book>> RV 4
RS <<book>>
<<-- stemstring
-->> stemstring: <<this>>
LP <<this>>
RD (4) <<this>>
ST (0,3) <<this>> RV 4
RS <<this>>
<<-- stemstring
-->> stemstring: <<is!>>
LP <<is!>>
RD (2) <<is>>
ST (0,1) <<is>> RV 2
RS <<is>>
LP <<!>>
NL <<!>>
<<-- stemstring
-->> stemstring: <<What.hast.thou.done?>>
LP <<What.hast.thou.done?>>
RD (4) <<What>>
ST (0,3) <<What>> RV 4
RS <<What>>
LP <<.hast.thou.done?>>
NL <<.>>
LP <<hast.thou.done?>>
RD (4) <<hast>>
ST (0,3) <<hast>> RV 4
RS <<hast>>
LP <<.thou.done?>>
NL <<.>>
LP <<thou.done?>>
RD (4) <<thou>>
ST (0,3) <<thou>> RV 4
RS <<thou>>
LP <<.done?>>
NL <<.>>
LP <<done?>>
RD (4) <<done>>
ST (0,3) <<done>> RV 4
RS <<done>>
LP <<?>>
NL <<?>>
<<-- stemstring

Показанные методы - печать диагностической информации в ключевых точках программы - это один из способов отладки такой программы, как эта. Альтернативой является пошаговое выполнение кода с помощью отладчика исходного кода - gdb или его эквивалента. Я, вероятно, чаще использую операторы печати, но я старый хрен, который считает IDE слишком сложным для использования (потому что они не ведут себя так, как я привык к командной строке).

Конечно, это уже не ваш код, но я думаю, что вы должны были выполнить большую часть отладки самостоятельно. Я благодарен, что вы сообщили о проблеме с моим кодом. Однако вам также необходимо научиться диагностировать проблемы в коде других людей; как его инструментировать; как охарактеризовать и найти проблемы. Затем вы можете сообщить о проблеме с точностью - «вы обидели ваше условие конца слова и ...».

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