复制文件中的单词

时间:2015-07-20 21:36:16

标签: c arrays string file

我需要将.text文件中的文本复制到另一个.txt文件中。我唯一的限制是,如果有任何重复,我只会将这个词放在新文件中一次。将原始文件中的单词分开的唯一值是空格。我正在考虑将整个文本复制成一个字符串,然后检查重复,但我不知道如何检查,如果是个好主意。你能帮助我一些想法吗?

2 个答案:

答案 0 :(得分:1)

策略

我们需要一个可以容纳无限数量单词的数据结构,让我们称之为 SET

此结构必须具有在其中添加单词的操作,我们将其称为 SET :: ADD
此类操作的返回值必须指示单词是否已添加或是否存在错误。

SET 结构的特点是一个单词只能添加一次,因此 SET :: ADD 返回两个错误值: GENERIC_ERROR 表示内部实现错误, DUPLICATE 表示尝试插入已存在的单词。

SET 结构还有一个初始化它的操作( SET :: INIT )和一个释放它的操作( SET :: FREE )。

然后我们从输入文件中读取单词,一次一个单词,然后将每个单词添加到 SET 结构中。
如果插入成功,我们将这样的单词写入输出文件,否则我们跳过此步骤。

伪算法

1. S = SET::INIT
2. FIN = OpenFile(input_file)
3. FOUT = OpenFile(output_file);
4. FOR EACH WORD IN FIN
4.1   IF SET::ADD(S, WORD) == SUCCESS THEN
4.1.1    WriteToFile(FOUT, WORD)
4.2   END IF
5. CloseFile(FIN);
6. CloseFile(FOUT);
7. SET::FREE(S);

策略

这里真正的努力是实现 SET 结构 数据结构由可以对其执行的操作以及此操作的前后条件定义。

所以理论上我们只需要在实现 SET :: ADD 时执行这些简单的操作:搜索单词是否已存在添加强>:

 1. FUNCTION SET::ADD(S, W)
 1.1   FOR EACH WORD IN S
 1.1.1   IF WORD == W THEN
 1.1.1.1   RETURN DUPLICATE
 1.1.2   END IF
 1.2   ADD W TO S

这两个步骤严重依赖于实施 对于具有此要求的数据结构,有许多实现,例如,非常天真的实现可以使用固定大小的指针数组。然而,这具有严重的缺点 更好的实现可以是链接列表,这让我们插入无限数量的单词,但需要线性时间进行插入和搜索!

所以我们进入了时间复杂的领域 我现在告诉你,这个结构可以用摊销的常数时间来实现。但是让我们从头开始。

链接列表的下一个逻辑步骤是:使用二进制树,使用哈希表
两者都可以同时进行插入和搜索,即两个操作都可以合并 第一个是 O log n )来插入和搜索,第二个是 O 1 )。<登记/> 第一个更有条理的让我们不仅可以进行搜索,还可以进行有序搜索。此功能对此问题没有用,因此我们应该选择哈希表。

我选择了树。这是因为二叉树让你比哈希表更好地练习指针和递归,是一个完好的类型(当你参加类型理论课程时,你会感谢我!)。

如果您不熟悉二叉树,请立即获取!向Google或您的老师询问!

实施

我们树的节点应该包含

  • 它存储的字样
  • 指向其左子树的链接(即指针)
  • 指向其右子树的链接(即指针)

最后两个是直截了当的,第一个可以实现为指针或固定大小的结构内数组。
我选择了后者,因为它使节点只需要一次调用malloc,这也得益于我们有一个单词的最大大小,因为我们用fscanf读取它。

代码的一些注释

  • 我使用了指针指针来实现add_word,这使得解决方案更优雅,但手边还有铅笔和纸张!
  • 此外,我(应该)确保使用strncpysnprintf并使用正确的len动态生成fscanf格式说明符,不会发生缓冲区溢出。 这是一种很好的做法 -
  • 我使用assert来检查格式说明符缓冲区的分配大小是否不够大,这是因为程序员可以计算出正确的大小,一旦代码编译它就会保持固定,所以不需要繁重的运行时检查。
  • fscanf一起使用的格式说明符的格式为%s ,其中 MAX_WORD_SIZE ,所以例如它结果是成为“%40s”。
  • 我使用递归来释放三个从叶子到根的乐趣。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>


/* Values returned by add_word */
/* The word has been added to the tree      */  
#define AW_ADDED         0  
/* The word cannot be added as malloc failed*/          
#define AW_ERROR        -1  
/* The word is a duplicate                  */  
#define AW_DUPLICATE     1      

/* Maximum size of a word                   */
#define MAX_WORD_SIZE 40        


/* Structure of the binary tree node        */
typedef struct node
{
    struct node* left;              /* Ptr to left (less than) branch       */
    struct node* right;             /* Ptr to right (greater than) branch   */
    char word[MAX_WORD_SIZE+1];     /* Word stored in the node              */
} node;


/*
    Add a word to the tree identified by the root pointer.
    This function allocate all the memory itself, the root of a tree is a pointer to a node structure.

    root is a pointer to the root (a ptr to ptr since the root may be updated)
    word is the word to add
*/
int add_word(node** root, const char* word)
{
    int compare;

    /* Traverse the tree until you find a null pointer (beware, we work with ptr to ptr) */
    while (*root)
    {
        /* Compare the current node word with the given one */
        compare = strcmp(word, (*root)->word);

        /* They are equal? Easy, just return the appropriate value */
        if (!compare)
            return AW_DUPLICATE;

        /* Move to the left of right based on the sign of the comparison */
        root = compare < 0 ? &((*root)->left) : &((*root)->right);
    }

    /* We found a null ptr to update with a ptr to a new node */

    /* Allocate memory */
    if (!(*root = malloc(sizeof(node))))
        return AW_ERROR;

    /* Init the new node */
    (*root)->left = (*root)->right = 0;

    /* Copy the given word, up to MAX_WORD_SIZE byte*/
    strncpy((*root)->word, word, MAX_WORD_SIZE);

    /* Set a null terminator on the last byte in the case the word is exactly MAX_WORD_SIZE char*/
    (*root)->word[MAX_WORD_SIZE] = 0;

    return AW_ADDED;
}

/*
   Free the memory used by the tree
   Set the pointers to NULL.
   Use recursion for didactic purpose, an iterative solution would consume less resources as
   this is NOT tail recursion.
 */
void free_tree(node** root)
{
    if (*root)
    {
        /* Go to children nodes */
        free_tree(&((*root)->left));
        free_tree(&((*root)->right));

        /* Free current node */
        free(*root);
        *root = NULL;
    }

}

int main()
{
    /* Open the files */
    FILE* fin = fopen("in.txt", "r");
    FILE* fout = fopen("out.txt", "w");

    /* Check the descriptors */
    if (!fin)
        return printf("Cannot open input file\n"), 1;

    if (!fout)
        return printf("Cannot open output file\n"), 2;

    /* This is out tree */
    node* root = NULL;
    /* This is the buffer for reading word from fin*/
    char new_word[MAX_WORD_SIZE+1];
    /* This is the buffer for creating fscanf format specifier*/
    char format[32];

    /* Create the fscanf format specifier */
    int char_used = snprintf(format, sizeof(format), "%%%ds", MAX_WORD_SIZE);
    assert(char_used + 1 <= sizeof(format));

    /* Read the file until the end */
    while (!feof(fin))
    {
        /* Read a word and add it to the tree, if it is added, write it to new file */
        if (fscanf(fin, format, new_word) && add_word(&root, new_word) == AW_ADDED)
            fprintf(fout, "%s ", new_word);                             
    }

    /* Close and exit */
    fclose(fin);
    fclose(fout);

    free_tree(&root);

    return 0;

}

答案 1 :(得分:0)

您可以尝试使用MD5或类似方法对字符串进行散列,然后将它们存储在二叉树中。应该具有相当低的平均时间复杂度。我不确定MD5的速度有多快;对于小字符串,可能有更好的哈希算法。

你可以将所有字符串存储在一个数组中,并且如果你不关心效率的话,每次你拿起一个新的字符串时都会使用strcmp。