在文本文件中为单词添加空格

时间:2016-11-21 03:48:50

标签: c

我有一个包含以下内容的简单文本文件:

dog
bear
panda
fish
elephant

但是,我希望他们所有人都有10个字符。

因此,我希望用' x'填充剩下的部分。像:

dogxxxxxxx
bearxxxxxx
pandaxxxxx
fishxxxxxx
elephantxx

到目前为止我所拥有的是:

int filterWords(){
    FILE *ptr_file;
    FILE *ptr_file2;
    char buf[1000];
    int i = 0;
    int numberSpaces;

    ptr_file =fopen("animals.txt","r");
    if (!ptr_file)
        return 1;

    while (fgets(buf,1000, ptr_file)!=NULL){
        if(strlen(buf) < 10){
            numberSpaces = 10 - strlen(buf)-1;
            memset(buf, 'x', numberSpaces);
            buf[strlen(buf)+numberSpaces] = '\0';
            ptr_file2 = fopen("newAnimal.txt", "a");
            fputs(buf, ptr_file2);
            fclose(ptr_file2);
        } 
    }

    fclose(ptr_file);
        return 0;
}

但是,当我尝试打开newAnimal.txt时,我得到了类似的内容:

dog
xxxbxxxear
pandaxxxf
ishxxxxxx
....

所有人都错了。我的代码出了什么问题?

4 个答案:

答案 0 :(得分:2)

首先,

memset(buf, 'x', numberSpaces);

应该是

memset(&buf[strlen(buf)], 'x', numberSpaces);

另外,你正在计算numberSpaces错误。而不是:

numberSpaces = 10 - strlen(buf)-1;

应该是:

numberSpaces = 10 - strlen(buf);

更正后,您需要将\ 0放在正确的位置。而不是:

buf[strlen(buf)+numberSpaces] = '\0';

变成:

buf[strlen(buf)] = '\0';

你的while循环现在看起来像:

while (fgets(buf,1000, ptr_file)!=NULL){
    if(strlen(buf) < 10){
        numberSpaces = 10 - strlen(buf);
        memset(&buf[strlen(buf), 'x', numberSpaces);
        buf[strlen(buf)] = '\0';
        ptr_file2 = fopen("newAnimal.txt", "a");
        fputs(buf, ptr_file2);
        fclose(ptr_file2);
    } 
}

答案 1 :(得分:2)

您的问题是一些与您密谋合作的一对一和类似的逻辑错误。让我们看一下主循环中的if语句,看看有什么问题:

if(strlen(buf) < 10){
    numberSpaces = 10 - strlen(buf)-1;
    memset(buf, 'x', numberSpaces);
    buf[strlen(buf)+numberSpaces] = '\0';
    ptr_file2 = fopen("newAnimal.txt", "a");
    fputs(buf, ptr_file2);
    fclose(ptr_file2);
}

首先要记住fgets会存储换行符,因此buf会有一个额外的换行符。这将增加长度(从strlen)到1.所以我们必须调整:

if (strlen(buf) < 11) {
    numberSpaces = 10 - (strlen(buf)-1); // otherwise - is done from the total, not from strlen
    memset(buf, 'x', numberSpaces); // still wrong...
    buf[strlen(buf)+numberSpaces] = '\0'; // still wrong...
    // fopen/fclose EVERY loop is terribly inefficient, but not wrong per se
    ptr_file2 = fopen("newAnimal.txt", "a");
    fputs(buf, ptr_file2);
    fclose(ptr_file2);
}

现在我们需要修复memset bufset问题,因为我们不想用'x'来设置字符串的开头,而是结束。同样地,我们不想以'\ 0'结束,我们想要'\ n \ 0'。我们也想在最后立即抛出,而不是在额外的数字空间等待之后......

if (strlen(buf) < 11) {
    numberSpaces = 10 - (strlen(buf)-1); 
    memset(buf + (strlen(buf)-1), 'x', numberSpaces); // now it will start from the correct spot
    buf[10] = '\n'; // we know how long we want our string to be ;)
    buf[11] = '\0';
    // fopen/fputs/fclose here (or outside the loop if you can)
}

最后要注意的一点是:在开始使用缓冲区之前,应确保缓冲区已填充为0 ...只需将char buf[1000];行转换为char buf[1000] = {0};即可。

另一个更优雅的解决方案是,仅在适用时通过buf[strcspn(buf, "\n")] = '\0';使用strcspn删除换行符。 strcspn不是一个众所周知的函数,但它会搜索所提供的字符并返回第一次出现的索引。它还会考虑'\ 0',所以如果它什么也找不到,它将返回字符串的长度。在校正换行符之后,您不再需要从所有strlen调用中减去1。

答案 2 :(得分:2)

还有第三个产品......请注意这里的内容,但选择其中一个作为接受的答案。

#include <stdio.h>
#include <string.h>

static int filterWords(const char *ifile, const char *ofile)
{
    FILE *ptr_file1 = fopen(ifile, "r");
    if (!ptr_file1)
    {
        fprintf(stderr, "Failed to open file %s for reading\n", ifile);
        return 1;
    }

    FILE *ptr_file2 = fopen(ofile, "w");
    if (!ptr_file2)
    {
        fprintf(stderr, "Failed to open file %s for writing\n", ofile);
        fclose(ptr_file1);
        return 1;
    }

    char buf[1000];
    while (fgets(buf, sizeof(buf), ptr_file1) != NULL)
    {
        buf[strcspn(buf, "\n")] = '\0';
        int len = strlen(buf);
        if (len < 10)
        {
            memset(buf + len, 'x', 10 - len);
            buf[10] = '\0';
        }
        fprintf(ptr_file2, "%s\n", buf);
    }

    fclose(ptr_file1);
    fclose(ptr_file2);
    return 0;
}

int main(void)
{
    printf("Status: %d\n", filterWords("animals.txt", "newAnimal.txt"));
    return 0;
}

注意:

  • 我已将文件名传递给函数,而不是硬编码。这也可以更容易地报告错误;你可以识别文件。
  • 当然,应在标准错误频道上报告错误。
  • 就个人而言,我发现ptr_fileptr_file2之间的(常见)不对称令人讨厌;我使用ptr_file作为一个,但在我需要这两个名字时使用ptr_file1ptr_file2。实际上,对于文件指针,我通常使用fp1fp2ifpofp,但这次我更接近原始代码。
  • 此代码检查两个文件是否已成功打开;你的代码假设每次都可以打开第二个文件,如果它突然无法打开,可能会崩溃。
  • 我假设是C99编译器,因此可以在需要时定义变量;如果你坚持使用C90编译器,请将声明移到顶部并将我的初始化更改为赋值。
  • 我使用buf[strcspn(buf, "\n")] = '\0';表示法来安全地删除缓冲区中的第一个(也是唯一的)换行符。
  • 然后我计算一次线的长度。
  • 如果该行短于10,那么我使用memset()将尾随部分设置为x并添加一个空字节。
  • 三次重复的10应该是一个命名常量,例如:

    enum { PAD_LENGTH = 10 };
    
  • 代码打开辅助文件一次,并将所有名称(即使那些不需要填充的名称)写入文件。

  • 两个文件都已明确关闭。
  • 偏执代码会检查输出文件是否已成功关闭并报告问题(如果有)。
  • 您可以决定是否在输入文件中报告错误 - 您可以使用ferror()来检测输入上的错误。
  • 在其他情况下,我可能会使用POSIX的getline()来读取该行。一个很大的优点是它返回行中的字符数,因此您不必以相同的方式搜索换行符(尽管需要担心没有尾随换行符的文件的最后一行)。 / LI>

输入:

dog
bear
panda
fish
elephant
alligator
rhinoceros
hippopotamus
East African Lion

输出:

dogxxxxxxx
bearxxxxxx
pandaxxxxx
fishxxxxxx
elephantxx
alligatorx
rhinoceros
hippopotamus
East African Lion

答案 3 :(得分:0)

下面的代码将解决您的问题:

int filterWords(void)
{
    FILE *ptr_file;
    FILE *ptr_file2;
    char buf[1000];
    int strLen = 0;
    int numberSpaces;

    ptr_file = fopen("animals.txt","r");
    if (!ptr_file)
        return 1;

    while (fgets(buf, 1000, ptr_file)!=NULL)
    {
        strLen = strlen(buf) - 1;
        if(strLen < 10){
            numberSpaces = 10 - (strLen);
            memset(&buf[strLen], 'x', numberSpaces);
            buf[strLen + numberSpaces] = '\n';
            buf[strLen + numberSpaces + 1] = '\0';
            ptr_file2 = fopen("newAnimal.txt", "a");
            fputs(buf, ptr_file2);
            fclose(ptr_file2);
        }
    }

    fclose(ptr_file);
        return 0;
}