Tech & IT/프로그래밍

CStdioFile: GetPosition, Seek 질문답변

해피콧 2009. 5. 22. 15:10
'); }
'); }
출처 : http://www.codeguru.com/forum/printthread.php?t=456659

nados29 July 6th, 2008 11:28 AM

CStdioFile: GetPosition, Seek
 
Dear all,
I am using CStdioFile to read a certain file.... however, there is a strange behaviour that I am facing... in the middle of reading the file (called MyFile) i do the following:

DWORD dw = MyFile.GetPosition();
MyFile.Seek(dw, 0);

I guess this should not make any difference in the current file position. Surprisingly, I noticed that the current file position changes.

I saw MyFile in the QuickWatch just before these 2 lines (to see the content from the current position), then did the same after these 2 lines of code, and found that the position is changing.

Please advise,
Thank you.

Paul McKenzie July 6th, 2008 11:39 AM

Re: CStdioFile: GetPosition, Seek
 
Quote:

Originally Posted by nados29
Dear all,
I am using CStdioFile to read a certain file.... however, there is a strange behaviour that I am facing... in the middle of reading the file (called MyFile) i do the following:

DWORD dw = MyFile.GetPosition();
MyFile.Seek(dw, 0);

How did you open the file? Did you open it as a text file or a binary file?

If you opened it as a text file, do not use these routines on files open in text mode. These functions such as GetPosition() and Seek() are only reliable on files open in binary mode. There is carriage-return/line feed translation that occurs when you use CStdioFile to open a file in text mode or if you use any file I/O function that opens a file in text mode. 

If you attempt to use Seek(), GetPosition(), fseek(), ftell(), etc. on text mode files, you'll just be fighting all day with the hidden CR/LF translation going on, and your program may never work correctly. If you really want to control where things are written or read in a file, right down to the exact byte within the file, the file must be opened in binary mode, meaning you must use the textBinary flag when opening the file.

I have never used CStdioFile, so I'm going by what the MSDN documentation states on the flags, and textBinary claims to open the file in binary mode.

Regards,

Paul McKenzie

nados29 July 6th, 2008 11:45 AM

Re: CStdioFile: GetPosition, Seek
 
Thank u for the information,
Actually I am opening the file in text mode; but I am using the Seek and GetPosition because I want to jump to a specific segment of the file, so ReadString is not sufficient to do that.

Any hints to do that in a text file?
Thank you

Paul McKenzie July 6th, 2008 11:53 AM

Re: CStdioFile: GetPosition, Seek
 
Quote:

Originally Posted by nados29
Thank u for the information,
Actually I am opening the file in text mode; but I am using the Seek and GetPosition because I want to jump to a specific segment of the file,

You can't open a file in text mode and reliably jump to a location of your choosing, since that file offset you're using isn't the true file offset. 

Jumping to specific locations in files is why binary mode is used. Since there is CR/LF translation going on behind the scenes in text mode, using Seek() is not going to work reliably. Binary mode is the way to open files and manipulate where things are being read or written to on the byte level.
Quote:

Any hints to do that in a text file?
Read the entire file into a buffer, change the buffer, write the buffer to a new file, rename new file to old file.

Regards,

Paul McKenzie

Newlena July 27th, 2008 10:29 AM

Re: CStdioFile: GetPosition, Seek
 
Paul McKenzie, Can you paste an example for your idea? Thank a lot.

i have a file:
item1 232
item2 5455
item3 12
....
itemn 2345

not i want to replace line "item2 5455" with "item 44", i just get "item 4455".
but if i want to replace line "item2 5455" with "item 444444", i get
"item2 444444tem3 12"

darwen July 27th, 2008 11:17 AM

Re: CStdioFile: GetPosition, Seek
 
You can't 'delete' characters from a file. Only overwrite what is already there.

You should do what PMK says : read the whole file into a buffer, change the buffer and then resave the file using the altered buffer.

What happens if you have an edit control with 'aa bb cc' in it, turn insert off (hit the insert key), position the cursor on the first 'b' and type 'hello' - what do you get ? You don't get 'aa hellobb cc' you get 'aa hello'. Files work the same way : any characters saved to the file only overwrite what's there (including newlines which is why you're getting the results you are).

Darwen.

darwen July 27th, 2008 01:03 PM

Re: CStdioFile: GetPosition, Seek
 
I was feeling generous : here's code which will load in the file, change an item and then save the file out again.

Code:

#include "stdafx.h"

#include <afxwin.h>
#include <vector>
#include <algorithm>

struct FileValues
{
    CString m_sName;
    CString m_sValue;
} ;

const wchar_t FileName[] = L"TextFile.txt";

void ReadFileValues(std::vector<FileValues> &values)
{
    CStdioFile file(FileName, CFile::modeRead);
    
    CString line;
    
    while (file.ReadString(line))
    {
        if (line.GetLength() > 0)
        {
            int indexOfSpace = line.Find(L' ');
            
            if (indexOfSpace > 0)
            {
                std::vector<FileValues>::iterator item = values.insert(values.end(), FileValues());
                item->m_sName = line.Left(indexOfSpace);
                item->m_sValue = line.Right(line.GetLength() - (1 + indexOfSpace));
            }
        }
    }
}

class PredicateNameEquals    
{
public:
    PredicateNameEquals(const CString &name)
        : m_sName(name)
    {
    }
    
    bool operator() ( const FileValues &value ) const
    {
        return value.m_sName == m_sName;
    }
    
private:
    const CString &m_sName;    
} ;

void ReplaceValueWith(std::vector<FileValues> &values, const CString &itemName, const CString &newItemName, const CString &newItemValue)
{
    std::vector<FileValues>::iterator item = std::find_if(values.begin(), values.end(), PredicateNameEquals(itemName));
    
    if (item != values.end())
    {
        item->m_sName = newItemName;
        item->m_sValue = newItemValue;
    }
}

class FunctorWriteToFile
{
public:
    FunctorWriteToFile(CStdioFile &file)
        : m_file(file)
    {
    }
    
    void operator() (const FileValues &values) const
    {
        m_file.WriteString(values.m_sName);
        m_file.WriteString(L" ");
        m_file.WriteString(values.m_sValue);
        m_file.WriteString(L"\n");
    }
    
private:
    CStdioFile &m_file;
};

void SaveFileValues(const std::vector<FileValues> &values)
{
    CStdioFile file(FileName, CFile::modeWrite);
    
    std::for_each(
        values.begin(),
        values.end(),
        FunctorWriteToFile( file ) );    
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::vector<FileValues> values;
    ReadFileValues(values);
    
    ReplaceValueWith(values, "item2", "item", "44");
    SaveFileValues(values);
    
    return 0;
}

I know it's mixing MFC and STL which isn't advisable, but you are using CStdioFile so I didn't want to give you an example using ifstream/ofstream which is the method I would normally use.

Darwen.