简体   繁体   中英

ifstream not reading updated file c++

I'm working on a project where I'm building an AVL tree using a file to store it. When I write to the file the data saves correctly but when I read the location for the data it gives me back the data for that was previously stored in the file.

Here's my code so far

#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);
}

heres my main

#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;
}

I am inserting from act 1 scene 1 of the collective works of shakespeare but the issue i have happens when inserting the first 'I' at this section

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);

at this point node2 contains 'ACT' and its right child is 1. this gets written to position 0. since a = 0 here node1 will be read from position 0. therefore what is read back should be the same as node2 but node1 does not have the correct right child. the right child it returns is -1 when it should be 1. I have stepped through the code and found that all of the correct data is written to the file but when the read is called it behaves like the last write never happened.

here is the structure of Node

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

does anyone know what might be happening? all guesses welcome I've been looking at this for several hours now and cant figure it out. Also, how can I fix this?

That's because ifstream only read the file at the first time and keep content in buffer for later read.

Since you will operate the file in both read and write mode, you may just only one file handler.

Step 1: We combine read and write stream handler to one, at AVL.h:

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

Step 2: Change AVL constructor:

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;
}

Step 3: Replace your inputTreeFile and outputTreeFile to this treeFile in read and write method:

// 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;
}

Now you shall see the expect result.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM