使用宽度和边距输出格式化的文本

时间:2018-10-13 21:20:58

标签: c

我正在尝试编写一个程序,该程序将接受以下输入并将其格式化并将其输出到文本文件。

Here is a picture of how it should work

?mrgn left:命令后的每一行都将从 左边距。请注意,该缩进必须包含在页面中 宽度。如果此命令未出现在输入文件中,则left的值 是0(零)。

到目前为止,我做了以下事情:

 while (fgets(line, MAX_LINE_LEN-1, infile) != NULL){/*Read the first line if it is not empty*/
    char last[MAX_WORD_LEN] = {0};
    char *p;
    for (p = strtok(line, " "); p; p = strtok(NULL, " ")){

        if(*last && strcmp(last, width)==0){
            iwidth = atoi(p);
            printf("width = %d\n", iwidth);
        }
        if(*last && strcmp(last, margin)==0){
            imargin = atoi(p);
            printf("margin = %d\n", imargin);
        }
        strncpy (last, p, MAX_WORD_LEN);
        if(iwidth != 0 || imargin != 0){
            printf("%s ", p);
        }else{
            printf("%s", line);
        }

    }
}

我能够将width和margin的值存储到一个变量中。我现在停留在如何指定所需的格式上。我做了一些研究,但找不到我想要的东西。请帮忙! 谢谢! 干杯!

1 个答案:

答案 0 :(得分:0)

在您花了几个小时处理问题后,让我给您一些指导(没有双关语),可以帮助您简化解决问题的方法。尽管您当然可以使用strtok来解析您的选项,并从输入文件中解析width,但是当您知道包含该选项的行格式为"?name value"时,确实不需要标记。< / p>

一种简化的方法是,知道您的文件包含该选项作为第一行,则只需读取整行(使用fgets或POSIX getline),并验证第一个字符为{{1 }},然后使用'?'从行中解析选项namevalue。 (您可以在{em> format-string 中包含sscanf,也可以仅从第二个字符开始进行解析。(我的选择)要实现此目的,您可以从类似于以下内容的内容开始: / p>

'?'

此时,在代码中,#include <stdio.h> #include <string.h> /* for strcmp, strlen */ #define OPT 32 /* if you need constants, #define one (or more) */ #define WDTH 78 #define MAXC 1024 void str_word_wrap (char *buf, int n); /* wrap function prototype */ int main (void) { char buf[MAXC] = "", /* buffer to hold words in file */ *p = buf, /* pointer to buf */ opt[OPT] = ""; /* buffer to hold option found in file */ int width = WDTH, /* variable holding width from option */ used = 0, /* number of chars used in buf */ val = 0; /* temp value to read option value from file */ /* option on line 1, read entire line */ if (!fgets (buf, MAXC, stdin) || *buf != '?') fputs ("error: unexpected file format, using default width.\n\n", stderr); /* parse option and value, compare option is "width", use val as width */ if (sscanf (buf + 1, "%s %d", opt, &val) == 2) { if (strcmp (opt, "width") == 0) width = val; } ... 包含第一行,buf包含选项名称,opt包含文件中指定的宽度(或默认宽度{{ 1}}(width),如果第一行不包含WDTH信息)。理想情况下,如果第一行不是有效的选项/值行,则只需从78中消除多余的空格,添加结尾的"?width val"并继续,但是代码留给您。

(注意:我只是将文件重定向到buf,所以我从' '而不是这里的文件中读取文件-但您的stdin也很好。您可以用{ {1}}我读过stdin的地方

由于您只想消除输入文件中的所有其他空格,而保留一个正常格式的段落,将其包装为指定的宽度,请使用infileinfile format指示符< / em>可以自动处理空格删除。 (对于stdin系列,fscanf和数字 format指定符,忽略前导空格,"%s"scanf则不会)。因此,将文件的其余部分读取到缓冲区中仅是读取文件中的每个单词,并跟踪缓冲区中有"%s"个字符的情况(这样您就知道下一个单词会适合),并且在将每个单词添加到缓冲区时,在每个单词之间添加"%c"空格)。

如果有帮助,可以使用"%[..]",也可以简单地使用指针,并在当前缓冲区末尾的末尾写一个used,然后以 nul终止在每次迭代后都超过该字符。无论哪种方式,只要跟踪您到目前为止已经使用了多少个字符以及所添加内容的' '(长度),然后就可以根据每个单词的长度来更新strcat计数。您可以执行以下操作:

' '

这时,您可以写出len的值和填充缓冲区的内容以进行检查,然后再将行包装到used。我只写了 while (scanf ("%s", p + used) == 1) { /* read each word, ignore WS */ size_t len = strlen (p + used); /* get length of word */ if (used + len + 2 >= MAXC) { /* make sure it fits with ' ' */ fputs ("warning: file truncated.\n", stderr); break; /* note you can refine to save 1-char space at end */ } *(p + used + len++) = ' '; /* add space at end of word */ *(p + used + len) = 0; /* nul-termiante after space */ used += len; /* update used with len */ } *(p + --used) = 0; /* overwrite final ' ' with nul-character */ 用完之后再输出换行,从而完成了程序的width功能,例如

width

所有剩下的事情就是完成输出缓冲区时将缓冲区包装为每行不超过width个字符的功能。我在上面的main()函数原型中提供了原型,并在我的原始评论中提供了有关通过仅使用 printf ("Wrapping file at width: %d\n\n", width); str_word_wrap (buf, width); /* wrap buffer at width chars/output */ return 0; } 长度的滑动窗口来封装缓冲区的方法来缩小缓冲区的方法的详细信息缓冲区,每次将其向下移到缓冲区中时,就会输出适合滑动窗口的单词。

要完成此任务,通常使用三个指针(我先命名为width指向当前字符的指针,str_word_wrap窗口的起始指针,width的结束指针方案是这样,首先将所有三个初始化为缓冲区的开头,然后用p遍历每个字符,直到sp指向单词之间的空格,并设置结束指针ep,每次遇到空格时,在每次迭代中检查p,其中p只是当前指针地址减去起始指针地址,它告诉您从起始位置移了多少个字符。如果等于或超过ep = p;,您就知道您最后一次将p - sp >= width(端点指针)设置为窗口中的最后一个空格标记要输出的最后一个单词的结尾。

剩下的一切就是将行输出到终点指针(和p - sp),然后将新的起点指针设置为终点指针之后的下一个字符,您可以设置终点指针到当前指针之后的一个(向前滑动窗口),然后重复一次。不需要花哨的东西。如下所示的效果很好:

width

完全放任您处理,

示例输入文件

ep

现在只需使用文件作为输入来运行程序,或将文件重定向到程序'\n'即可:

使用/输出示例

void str_word_wrap (char *buf, int n)
{
    char *p = buf,      /* pointer to current char */
        *sp = buf,     /* pointer to start of line to print */
        *ep = buf;     /* pointer to end of line to print */

    for (; *p && *p != '\n'; p++) { /* loop over each char (omit '\n')*/
        if (*p == ' ')              /* if space, set ep */
            ep = p;
        if (p - sp >= n) {          /* if wrap length 'n' reached */
            while (sp < ep)         /* loop outputting chars sp -> ep */
                putchar (*sp++);
            putchar ('\n');         /* tidy up with '\n' */
            sp = ++ep;              /* set start to next after end */
            ep = ++p;               /* set end to next after current */
        }
    }
    while (*sp && *sp != '\n')      /* output last line of chars */
        putchar (*sp++);
    putchar ('\n');                 /* tidy up with final '\n' */
}

仔细研究一下,如果您有任何疑问,请告诉我。所有这些都归结为基本的指针算法,并在迭代缓冲区中的每个字符以从中提取所需的任何特定信息时跟踪缓冲区中的位置。您经常会听到在缓冲区上或下的“遍历指针”。使用滑动窗口只是指针的移动,同时跟踪您开始的固定点,并将步行限制为不超过某个固定宽度的字符,然后执行任何操作您需要反复进行直到结束。

帮助“了解指针”

由于您在问题下方的评论中提到您“正在学习指针”,因此请从基础开始:

指针只是一个普通变量,它以其他值作为地址。换句话说,指针指向可以找到其他内容的地址。在通常情况下,您会想到一个包含立即数的变量,例如$ cat dat/taggedparagraph.txt ?width 30 While there are enough characters here to fill at least one line, there is plenty of white space that needs to be eliminated from the original text file. ,指针将仅保存stdin存储在内存中的地址,例如$ ./bin/wrapped_tagged_p < dat/taggedparagraph.txt Wrapping file at width: 30 While there are enough characters here to fill at least one line, there is plenty of white space that needs to be eliminated from the original text file.

要引用指针所在地址的值,请使用指针名称前的一元int a = 5;字符取消引用。例如,5拥有int *b = &a;的地址(例如'*'指向b),因此,要获取a拥有的地址的值,您只需取消引用 b,例如a

无论指针指向什么类型的对象,它都以相同的方式工作。之所以可以这样工作,是因为指针的b控制着指针的算术运算,例如:使用b指针,*b指向下一个字节,对于type指针(正常的4字节整数),char *将指向下一个{{1} }在pointer+1之后的4个字节处偏移。 (因此,一个指针只是一个指针。...int *自动处理算术的地方)

在C语言中处理字符串时,可以从字符串的开头到结尾进行迭代,检查每个字符,并在到达每个字符串末尾的 nul-终止字符时停止。此 nul-character 用作字符串结尾的 sentinel 。您将看到它表示为pointer+1或只是纯int。两者是等效的。 ASCII字符pointer具有整数值type

一个简单的指针指针示例可能有助于巩固这一概念:

'\0'

使用/输出示例

0