0

这段代码是一个更大项目的一部分。我告诉你这是因为它是相关的。我正在开发一个小型(codewise)数据库来跟踪我所有的照片。我正在设计它,以便以后可以扩展程序以存储我所有论文中的引文。

无论好坏,我都选择了一个具有固定宽度字段的简单平面文件系统。这可能不是对内存的最有效使用,并且文件大小会变得混乱,但它具有一些超级优势:没有分隔符,因此文件可以通过良好的格式轻松保持人类可读,以及使用简单的数组索引轻松访问数组。

我特别喜欢最后一部分,因为它在我的技能范围内。关于这个数据库的一件事是我需要能够输入很多条目 FAAAA...ST!瓶颈出现在用户输入部分,而不是内存,也不是磁盘空间,所以我专注于让输入变得快速和简单。

我了解到在 C 中获取用户输入可能是一件棘手的事情。C 的字符串处理库有很多可以用来产生缓冲区溢出的函数。所以……对于这段代码,我实施了 Robert C. Seacord在 C 和 C++ 中的安全编码中的一些建议,特别是“字符串和缓冲区溢出”一章。

这是一个链接:http ://www.informit.com/articles/article.aspx?p=2036582&seqNum=5

Seacord 建议使用 fgets() 处理输入行,虽然可以安全地执行,但具有性能限制。(我喜欢快。不是吗?)他进一步建议使用 getchar() 代替。这就是我所做的。

这是他对使用 while 循环安全地使用 getchar() 的建议:

while(( (ch = getchar()) != \n) && ch != EOF)

在下面的代码中,我稍微调整了一下。

我需要 I/O 做几件事。首先,如果输入的输入太长,我希望它被截断。即使我可能是唯一的用户,我也可能会犯错误。其次,如果输入比字段宽度短(主要是这种情况),我想在该字段右侧填充空格。

这就是我遇到的问题。稍后再谈。

这个空白使平面文件看起来干净,并且再次使索引变得非常容易。我只需要按“Enter”键并按顺序进入下一个条目,因为我知道计算机已经按照我想要的方式格式化了数据。

(这实际上是一个粗略的实现 EF Codd 的原则,即数据必须被屏蔽以防止直接访问。在进入存储之前,必须对所有内容进行检查、抛光、解析等。这样可以防止数据损坏。)

在这段代码中,我已经删除了最后的所有内容,因为它只是噪音,而且我非常讨厌尝试阅读其他人的代码,他们将整个该死的程序与各种无关的东西而不是给他们带来麻烦的部分一起发布. 同时,我喜欢发布一个可以选择、复制、粘贴、保存和编译的完整程序。所以我在这里做了。我已经在评论和我的小检查中留下了我将取消评论以确保一切正常,然后再次注释掉,所以这不是最简单的代码,但是爱因斯坦那句著名的关于简单的名言是什么?

无论如何,我知道这有点长,但我想勾勒出我正在使用的设计原则。你可能有一个有用的批评。我肯定会采取愚蠢的做法。

我遇到的确切问题是如何用正确数量的空白填充这些字段。

确实想出了一个解决方案。有点。

但这对我来说似乎是一个黑客行为。

我只是怀疑有一种更有效、更快或更优雅的方式来完成我想要做的事情。“hack”部分是调用第二个打印函数并使用字符数和 maxlength 常量之间的差异在数据后添加空格。见第 27-39 行。它有效……但是?

我不应该直接填充数组吗?

想不通!

这是代码:

#include <stdio.h>
#include <stdlib.h>

/** array-ops/5.c
    Template for inputting data from stdin using getchar().
        Sets arraymax to prevent overflow, truncates array if smaller than 
    arraymax, right pads short entries with spaces, and quits gracefully. 
    (Really? You're /sure/ about that last?) */

#define ARRAYMAX 8

int main(int argc, char *argv[])
{
    int ch;
    int count;
    char array[ARRAYMAX];

    ch = getchar();
    count = 0;
    // no overflows, unclosed processes, or extra keystrokes needed
    while(count < ARRAYMAX && ch != '\n' && ch != EOF) {
        array[count++] = ch;

        ch = getchar();
    }

    int diff = (ARRAYMAX - count);
    //printf("count: %d\n", count); // check
    //printf("diff: %d\n", diff); // check again. off-by-one?

    int i;
    for(i = 0; i < count; i++) {
        printf("%c", array[i]);
    }

    int j;
    for(j = 0; j < diff; j++) {
        printf("%s", " ");
    }

    //printf("|\n"); // check, spaces really there?
    printf("\n");

    return 0;
}

顺便说一句,我在发布之前真的搜索过这个问题的答案。随意打倒我,但似乎每个人都试图解决一个略有不同的问题,尤其是对数据保护和缓冲区溢出的漠不关心。所以我不认为这是一个重复的问题。

[编辑] 这是修改后的代码。它结合了 Joachim 的解决方案和 if-else 循环来隔离截断的字符串。它仍然不是最好的,但...

#include <stdio.h>
#include <stdlib.h>

/** array-ops/5r.c
    Template for inputting data from stdin using getchar().
        Sets arraymax to prevent overflow, truncates array if smaller than 
    arraymax, right pads short entries with spaces, and quits gracefully. */

#define ARRAYMAX 8

int main(int argc, char *argv[])
{
    int ch;
    int count;
    char array[ARRAYMAX];

    ch = getchar();
    count = 0;
    // no overflows, unclosed processes, or extra keystrokes needed
    while(count < ARRAYMAX && ch != '\n' && ch != EOF) {
        array[count++] = ch;

        ch = getchar();
    }

    int diff = (ARRAYMAX - count);
    printf("count: %d\n", count); // check
    printf("diff: %d\n", diff); // check again for off-by-one

    if(count == ARRAYMAX) {
        printf("%.*s", ARRAYMAX, array);
    } else {
        printf("%.*s%*c", count, array, diff, ' ');
    }

    printf("|--array ends there\n"); // check, spaces really there?
    //printf("\n");

    return 0;
}
4

2 回答 2

1

在您的情况下,最简单的方法是初始化array为所有spaces(十六进制0x20)。然后,无论输入什么,您array的始终space-padded8-chars. 无论输入的长度如何,这都有效,并且不必担心输入的字符数或计算填充的长度。此外,它不依赖于输入的字符。如果用户(您)只需点击[enter],您仍然会得到8-char填充数组。:

#include <stdio.h>
#include <stdlib.h>

#define ARRAYMAX 8

int main()
{
    int ch;
    int count = 0;
    char array[ARRAYMAX] = { 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20 };
    int i = 0;

    while((ch = getchar()) && count < ARRAYMAX && ch != '\n' && ch != EOF)
        array[count++] = ch;

    printf("\n         01234567\n");

    printf(" array: '");
    for (i = 0; i < ARRAYMAX; i++)
        printf("%c", array[i]);

    printf ("'\n\n");

    return 0;
}

输出:

$ ./bin/padstr8
Hi

         01234567
 array: 'Hi      '

$ ./bin/padstr8
It's all Good

         01234567
 array: 'It's all'

注意:如果你使用gcc,你可以初始化所有元素array

char array[ARRAYMAX] = { [0 ... 7] = 0x20 }; 

或更具可读性:

char array[ARRAYMAX] = { [0 ... 7] = ' ' };

这减少了对每个元素的初始化。


这个练习让我开始了一个切线,看看我可以写什么类型的函数char array,无论是分配(在stackheap),还是根本不分配,只是作为NULL指针传递,以及一个field size允许我填充固定字段大小并使其在main(). 我做了一些修改,并找到了一些东西,如果我没有找到更多隐藏的陷阱,它似乎做得不错。它被传递以供深思:

#include <stdio.h>
#include <stdlib.h>

#define ARRAYMAX 8

/** fill 'str' with input from stdin rt-padded to 'szfld'.
*  if 'str' exists and has storage, 'str' is filled with
*  input up to a maximum size of 'szfld' chars. If input
*  is less than 'szfld', 'str' is rt-padded with spaces.
*  if 'str' is 'NULL', 'szfld' chars are allocated.
*  NOTE: 'str' is NOT null-terminated. (intentionally)
*/
char *fillfield (char *str, int szfld)
{
    char ch = 0;
    int  count = 0;
    int  i = 0;

    szfld = (szfld > ARRAYMAX) ? ARRAYMAX : szfld;

    if (str)
        for (i = 0; i < szfld; i++)
            str[i] = 0;
    else
        str = calloc (szfld, sizeof (char));

    printf ("\n Input: ");
    while((ch = getchar()) && count < ARRAYMAX && ch != '\n' && ch != EOF)
        str[count++] = ch;

    if (count >= ARRAYMAX && ch != '\n')
        while ((ch = getchar()) != '\n' && ch != EOF) ;

    char *p = str + szfld - 1;
    while (!*p && p >= str) *p-- = 0x20;

    return str;    
}

int main() {

    char field_1[6];
    fillfield (field_1, 6);                 /* fill existing array */

    char *field_2 = fillfield (NULL, 6);    /* allocate/fill array */

    printf ("\n field_1: '%.*s'\n", 6, field_1);
    printf (" field_2: '%.*s'\n\n", 6, field_2);

    if (field_2) free (field_2);            /* clean up allocation  */

    return 0;
}

输出:

$ ./bin/padstrf

 Input: hi

 Input: hi

 field_1: 'hi    '
 field_2: 'hi    '

$ ./bin/padstrf

 Input:  hi

 Input:  hi

 field_1: ' hi   '
 field_2: ' hi   '

$ ./bin/padstrf

 Input: truncate

 Input: truncate

 field_1: 'trunca'
 field_2: 'trunca'

$ ./bin/padstrf

 Input:

 Input:

 field_1: '      '
 field_2: '      '
于 2014-11-22T05:23:48.273 回答
0

If you see e.g. this printf reference, you will might notice the * format modifier, that can be used to set the field width or precision through an argument.

Can be used such as

printf("%.*s%*c\n", count, array, diff, ' ');

This will print count characters from array, then print one space right-justified by diff characters, and end it with a newline.

For an input of e.g. "ab\n", it should print "ab " followed by a newline.

于 2014-11-22T05:03:20.313 回答