ifstream未读取更新的文件c++

ifstream not reading updated file c++

本文关键字:文件 c++ 更新 读取 ifstream      更新时间:2023-10-16

我正在处理一个项目,我正在使用文件来存储AVL树。当我写入文件时,数据会正确保存,但当我读取数据的位置时,它会将以前存储在文件中的数据返回给我。

这是我到目前为止的代码

#include "stdafx.h"
#include "AVL.h"
#include <string>
#include <iostream>
#include <fstream>
// When tree is initialized open the input and output files
AVL::AVL(std::string treeFilePath)
{
    inputTreeFile.open(treeFilePath, std::ios::binary);
    if (inputTreeFile.fail())
        std::cout << "failed to open input file" << std::endl;
    outputTreeFile.open(treeFilePath, std::ios::binary | std::ios::trunc);
    if (outputTreeFile.fail())
         std::cout << "failed to open output file" << std::endl;
}
// close the tree files when destructing the tree
AVL::~AVL()
{
    inputTreeFile.close();
    outputTreeFile.close();
}
// writes the node to a given location of the output file
// based on the index of that node
void AVL::writeToDisk(Node node)
{
    outputTreeFile.seekp(node.index * sizeof(Node));
    char * buffer = (char *)&node;
    outputTreeFile.write(buffer, sizeof(Node));
    outputTreeFile.flush();
    if (outputTreeFile.fail())
        std::cout << "failed to write to output file" << std::endl;
}
// reads a node from the file given an index of the file in order to generate an offset
AVL::Node AVL::readFromDisk(int index)
{
    Node node;
    if (index == -1)
        return node;
    inputTreeFile.seekg(index * sizeof(Node));
    inputTreeFile.read((char *)&node, sizeof(Node));
    if (inputTreeFile.fail())
        std::cout << "failed to read from input file" << std::endl;
    return node;
 }
 // inserts a node into the tree and then checks balance factors to decide if a rotation is needed
 // to maintain the balance of the tree
 void AVL::insert(char input[30])
 {
        // If the root is null we only need to do a dumb insert and nothing else
    if (root == -1)
    {
        root = 0;
        node1.balanceFactor = 0;
        node1.count = 1;
        node1.index = 0;
        node1.leftChild = -1;
        node1.rightChild = -1;
        strcpy_s(node1.value, input);
        uniqueInserts++;
        writeToDisk(node1);
        return;
    }
    int p; // our current location on the tree
    int q; // lags behind current node
    int a; // the last parent of current node that had a balance factor of +- 1
    int f; // lags behind recent nonzero
    p = a = root;
    q = f = -1;
    node1 = readFromDisk(root);
    // while the current node is not at the child of a leaf or a duplicate value keep traversing through the tree
    // keeping track of the most recent nonzero balance factor node
    while (p != -1)
    {
        if (strcmp(input, node1.value) == 0)
        {
            node1.count++;
            writeToDisk(node1);
            return;
        }
        if (node1.balanceFactor != 0)
        {
            a = p;
            f = q;
        }
        q = p;
        p = (strcmp(input, node1.value) < 0) ? node1.leftChild : node1.rightChild;
        node1 = readFromDisk(p);
    }
    // Now the previous node is a leaf and the current node is the child of that leaf so we need
    // to create a new node to insert
    // node to insert is y
    node1.balanceFactor = 0;
    node1.count = 1;
    int y = node1.index = uniqueInserts;
    node1.leftChild = -1;
    node1.rightChild = -1;
    strcpy_s(node1.value, input);
    uniqueInserts++;
    writeToDisk(node1);
    int b; // this is the child of the most recent nonzero balance factor in the direction of the potential rotation
    int displacement; // this is used to correct balance factors later
    // we need to know if the new node we are inserting is the left or the right child of the previous node so we
    // can have the correct child pointer point to the new node
    node2 = readFromDisk(q);
    if (strcmp(input, node2.value) < 0)
        node2.leftChild = y;
    else
        node2.rightChild = y;
    writeToDisk(node2);
    // if the value of the node we just inserted is less than that of the most recent nonzero balance factor node
    // then we went left so the pivot needs to be the left child else its the right child
    // the displacement is set based on the direction taken
    node1 = readFromDisk(a);
    if (strcmp(input, node1.value) > 0)
    {
        p = node1.rightChild;
        b = p;
        displacement = -1;
    }
    else
    {
        p = node1.leftChild;
        b = p;
        displacement = 1;
    }
    // then we traverse down from the most recent nonzero balance factor node to the node we inserted setting balance factors
    // on the way down
    while (p != y)
    {
        node1 = readFromDisk(p);
        if (strcmp(input, node1.value) > 0)
        {
            node1.balanceFactor = -1;
            p = node1.rightChild;
        }
        else
        {
            node1.balanceFactor = 1;
            p = node1.leftChild;
        }
        writeToDisk(node1);
    }

    node1 = readFromDisk(a);
    // if the tree was completely balanced recentNonZero will be at the root of the tree and our insert will
    // have pushed the tree slightly out of balance
    if (0 == node1.balanceFactor)
    {
        node1.balanceFactor = displacement;
        writeToDisk(node1);
        return;
    }
    // if the most reent nonzero balance factor is opposite the displacement then we put the tree back in balance 
    if (node1.balanceFactor == -displacement)
    {
        node1.balanceFactor = 0;
        writeToDisk(node1);
        return;
    }
    node2 = readFromDisk(b);
    // At this point we have thrown the tree out of balance by inserting
    // The displacement tells us the first direction of the rotation and the most recent non-zero balance factor node (b) 
    // tells us the second direction
    if (displacement == 1)
    {
        if (node2.balanceFactor == 1) //LL
        {
            // set a's left child to b's right and b's right to a
            // then fix balance factors
            node1.leftChild = node2.rightChild;
            node2.rightChild = a;
            node1.balanceFactor = node2.balanceFactor = 0;
        }
        else //LR
        {
            // create a new node c that is b's right child
            node3 = readFromDisk(node2.rightChild);
            // for this rotation the order of a, b, and c are b < c < a
            // so c gets pulled up to the middle and sets its children to b (left) and a (right)
            // this cuts off c's children though so prior to this c's left needs to be attached as b's right
            // and c's right is attached as a's left
            // then attach c's children to a and b
            node1.leftChild = node3.rightChild;
            node2.rightChild = node3.leftChild;
            // then set c's children to b and a
            node3.leftChild = b;
            node3.rightChild = a;
            // then we set a and b's balance factors to 0 and correct one of them depending on what
            // c's balance factor
            node1.balanceFactor = node2.balanceFactor = 0;
            if (node3.balanceFactor == 1)
                node1.balanceFactor = -1;
            else if (node3.balanceFactor == -1)
                node2.balanceFactor = 1;
            // then c's balance factor is fixed and set to 0
            node3.balanceFactor = 0;
            writeToDisk(node3);
            b = node3.index; // this is for reattaching the subtree to the proper parent
        }
    }
    else // again the next parts are symmetric so almost all the operations are just flipped
    {
        if (node2.balanceFactor == -1) //RR
        {
            node1.rightChild = node2.leftChild;
            node2.leftChild = a;
            node1.balanceFactor = node2.balanceFactor = 0;
        }
        else //RL
        {
            node3 = readFromDisk(node2.leftChild);
            node1.rightChild = node3.leftChild;
            node2.leftChild = node3.rightChild;
            node3.rightChild = b;
            node3.leftChild = a;
            node1.balanceFactor = node2.balanceFactor = 0;
            if (node3.balanceFactor == 1)
                node2.balanceFactor = -1;
            else if (node3.balanceFactor == -1)
                node1.balanceFactor = 1;
            node3.balanceFactor = 0;
            writeToDisk(node3);
            b = node3.index;
        }
    }
    writeToDisk(node1);
    writeToDisk(node2);
    // if the parent of the recent non zero balance factor node was null then there were no nodes with a nonzero balance
    // or the only one was the root. in either case the recent non zero was the root so whatever is in position b needs to 
    // be set as the root
    if (f == -1)
    {
        root = b;
        return;
    }
    node3 = readFromDisk(f);
    // if the left child of the recent nonzero BF parent node is the recent nonzero node we need to attach the new
    // root of the subtree (b) in a's place
    if (node3.leftChild == a)
        node3.leftChild = b;
    else // otherwise its the right child that needs reattached
        node3.rightChild = b;
    writeToDisk(node3);
}

这是我的主要

#include "stdafx.h"
#include "AVL.h"
#include <fstream>
#include <iostream>
#include <chrono>
using namespace std;
int main()
{
    string inputFilePath = "C:\Users\DMCar\Desktop\a1s1.txt";
    // set of delimiters for reading in the file
    char delimiters[11] = { 9 , 10 , 13 , 32 , '.' , ',' , '!' , ';' , ':' , '(' , ')' };
    AVL avl("C:\Users\DMCar\Desktop\test.txt");
    std::ifstream inputStream;
    inputStream.open(inputFilePath, std::ios::binary); // binary flag is set to read the file one byte at a time
                                              // if we couldn't open the file, let the user know and return
    if (inputStream.fail())
    {
        std::cout << "Could not open file" << std::endl;
        return 0;
    }
    bool isDelimiter = false;
    char nextChar;
    inputStream.get(nextChar);
    char input[30]{ 0 };
    int index = 0;
    // keep getting bytes until we have reached the end of the file
    while (!inputStream.eof())
    {
        // loop through the delimiters to check if the character read is one of them
        for each (char delimiter in delimiters)
        {
            // if the character is a delimiter we check to see if the word is empty
            // if the word is not empty it is sent to the trees insert function
            if (nextChar == delimiter)
            {
                if (input[0])
                {
                    cout << input << endl;
                    avl.insert(input);
                }

                memset(input, 0, sizeof(input));
                index = -1;
                isDelimiter = true;
            }
        }
        // if the character was not a delimiter we need to append it to next word
        if (!isDelimiter)
            input[index] = nextChar;
        index++;
        isDelimiter = false; // reset is delimiter
        if (inputStream.eof())
        {
            int i = 1;
            i++;
        }
        inputStream.get(nextChar); // try to read the next character
    }
    if (input[0] != 0)
    {
        avl.insert(input);
    }
    return 0;
}

我插入了莎士比亚集体作品的第1幕第1幕,但我在这一部分插入第一个"我"时遇到了问题

writeToDisk(node2);
// if the value of the node we just inserted is less than that of the most recent nonzero balance factor node
// then we went left so the pivot needs to be the left child else its the right child
// the displacement is set based on the direction taken
node1 = readFromDisk(a);

此时node2包含"ACT",其右子节点为1。这将被写入位置0。由于这里a=0,节点1将从位置0读取。因此,读回的内容应该与node2相同,但node1没有正确的子节点。它返回的正确子项是-1,而它应该是1。我仔细阅读了代码,发现所有正确的数据都写入了文件,但当调用read时,它的行为就像上次写入从未发生过一样。

这是节点的结构

struct Node {
    int leftChild = -1;
    int rightChild = -1;
    char value[30];
    unsigned int count = 0;
    int balanceFactor = 0;
    int index = -1;
};

有人知道可能发生什么吗?欢迎所有的猜测我已经看了好几个小时了,都想不通。此外,我该如何解决此问题?

这是因为ifstream只在第一次读取文件,并将内容保存在缓冲区中以备以后读取。

由于您将在读取和写入模式下操作文件,因此可能只有一个文件处理程序。

步骤1:我们将读写流处理程序合并为一个,在AVL.h:

//std::ifstream inputTreeFile;
//std::ofstream outputTreeFile;
std::fstream treeFile;

第2步:更改AVL构造函数:

AVL::AVL(std::string treeFilePath)
{
    //inputTreeFile.open(treeFilePath, std::ios::binary | std::ios::);
    //if (inputTreeFile.fail())
    //  std::cout << "failed to open input file" << std::endl;
    //outputTreeFile.open(treeFilePath, std::ios::binary | std::ios::trunc);
    //if (outputTreeFile.fail())
    //  std::cout << "failed to open output file" << std::endl;
    treeFile.open(treeFilePath, std::ios::binary | std::ios::trunc |     std::ios::in | std::ios::out);
    if (treeFile.fail())
        std::cout << "failed to open file" << std::endl;
}

步骤3:将你的inputTreeFile和outputTreeFile替换为这个树文件读写方法:

// writes the node to a given location of the output file
// based on the index of that node
void AVL::writeToDisk(Node node)
{
treeFile.seekp(node.index * sizeof(Node));
char * buffer = (char *)&node;
treeFile.write(buffer, sizeof(Node));
treeFile.flush();
if (treeFile.fail())
    std::cout << "failed to write to output file" << std::endl;
}
// reads a node from the file given an index of the file in order to generate an offset
AVL::Node AVL::readFromDisk(int index)
{
    Node node;
    if (index == -1)
        return node;
    treeFile.seekg(index * sizeof(Node));
    treeFile.read((char *)&node, sizeof(Node));
    char * p = (char *)&node;
    if (treeFile.fail())
        std::cout << "failed to read from input file" << std::endl;
    return node;
}

现在你将看到预期的结果。