Re-organising a vector according to the filename in C++ MFC

Hey guys,

I am trying to re-organise a series of filenames I have stored into a vector.
For example:
TESTDATA1
TESTDATA2
TESTDATA3
....
....
TESTDATA2000
etc...

When my program reads in a list of files from a directory, it'll ordering in based on the numerical value of the position of that character. I don't know if I explained it properly by it will store the filenames in an order such as this:
TESTDATA1
TESTDATA10
TESTDATA100
TESTDATA1000
TESTDATA1001
etc...

Does anyone know how to make it so that it'll store my files in numerical order similar to my first example of TESTDATA1, TESTDATA2, etc.

Thank you very much in advance.
LVL 1
mr_stevieAsked:
Who is Participating?

[Webinar] Streamline your web hosting managementRegister Today

x
 
phoffricConnect With a Mentor Commented:
OK, if this is not academic in nature..
>> For your sort method, what is your cmpString() function?

Unless you have a very large numbers of filenames, I don't think there is going to be much of a performance difference.

>> I'm dealing with CStrings
Replace string with CStrings
Use cmpString logic using the CStrings version getting string length (instead of size()), and use CStrings version of < or strcmp when CStrings length are the same.

Probably using CString::Compare:
http://msdn.microsoft.com/en-us/library/aa314313(VS.60).aspx




////////////////////////////
// The vector approach:
////////////////////////////
vector<string> names;
names.push_back("TESTDATA1.txt"); ...
sort(names.begin(), names.end(), cmpString);

bool cmpString(string &x, string &y) {
   if( x.size() < y.size() ||
      (x.size() == y.size() && x.compare(y) < 0 ) ) {
         return true;
   }
   else {
      return false;
   }
}
////////////////////////////
//The set approach: (no sort required)
////////////////////////////
set<string, classcomp> filenames;
bool cmpString(string &x, string &y) {
   if( x.size() < y.size() ||
      (x.size() == y.size() && x.compare(y) < 0 ) ) {
         return true;
   }
   else {
      return false;
   }
}
filenames.insert("TESTDATA1.txt"); ...
// and then to print out results of insert operations:
for_each( filenames.begin(), filenames.end(), printNames );
void printNames( string name ) { cout << name << endl; }

Open in new window

0
 
phoffricCommented:
I don't know MFC. Is STL vector OK?
0
 
phoffricCommented:
Following would be easy to do using STL.
If this is the original vector:
TESTDATA1
TESTDATA3
TESTDATA10
TESTDATA1000
TESTDATA1001
TESTDATA2000
TESTDATA40
TESTDATA100
TESTDATA2

then is this the desired result after reorganizing?
TESTDATA1
TESTDATA2
TESTDATA3
TESTDATA10
TESTDATA40
TESTDATA100
TESTDATA1000
TESTDATA1001
TESTDATA2000

Open in new window

0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
mr_stevieAuthor Commented:
Yeah, a STL vector will be fine.
 I just need an idea of how I am going to re-arrange the filenames :)
0
 
mr_stevieAuthor Commented:
To some extent, yes.
However when the function reads the filenames, it will go up to the digit and order it that way (I'm not sure if I'm being clear)...

For example, it will look like this:
TESTDATA1
TESTDATA10
TESTDATA100
TESTDATA1000
TESTDATA1001
... skip a few hundred...
TESTDATA2
TESTDATA20
TESTDATA200
TESTDATA2000
TESTDATA2001
etc

If there is a way of actually reading how it appears in Windows (in order how I desire) into a vector instead of getting all the filenames first into a vector then re-arranging the vector as the first method would be less time consuming maybe :)

Hope someone knows what I'm talking about :)
0
 
mr_stevieAuthor Commented:
Maybe I should add the code I'm using:
// Create vector of filenames
WIN32_FIND_DATA findFileData;
HANDLE hFind = FindFirstFile( "C:\\TEST\\ + _T("\\*.csv") , &findFileData );

// v<CString> is a vector containing my filenames
			
v.push_back( convertWCharArrayToString( l + _T("\\\\") + findFileData.cFileName) );
			
// Keep going through the directory to find the next file.
while( FindNextFile(hFind, &findFileData) )
{
	v.push_back( convertWCharArrayToString( l + _T("\\\\") + findFileData.cFileName) );
}
			
// Close
FindClose(hFind);

Open in new window

0
 
pgnatyukCommented:
@phoffric: it is about STL.

@mr_stevie
You can use sort from the STL algorithm:
http://www.cplusplus.com/reference/algorithm/sort/

You can use this sort with a function or class as the parameter that will make the comparison. In the end of this article you will find an example:
http://www.cs.brown.edu/~jak/proglang/cpp/stltut/tut.html
Or here (very short and simple):
http://www.switchonthecode.com/tutorials/cplusplus-tutorial-sorting-stl-containers


0
 
phoffricCommented:
>> as the first method would be less time consuming maybe :)
OK, I didn't catch that from the original question. But let's see how the performance is doing it by reading in the way I did it. And let's see if this gives you the answer you are looking for.

Given names defined as:
    vector<string> names;
you use the sort to reorganize the vector.
You just need to write cmpString() function to suit your needs.
The cmpString I have assumes that
     if the length of x > length of y, then we want x > y
     if the length of x < length of y, then we want x > y
     if the length of x == length of y, then if x.compare(y) < 0, then we want x < y
bool cmpString(string &x, string &y);
sort(names.begin(), names.end(), cmpString);

Open in new window

0
 
mr_stevieAuthor Commented:
I forgot about sort. It would have worked in the above scenario however, I should have mentioned the strings have an extension attached to it...

For example:
TESTDATA1.txt
TESTDATA10.txt
TESTDATA100.txt
TESTDATA1000.txt
TESTDATA1001.txt
etc...

That is why I was wondering if there was anything in the MFC library that will let me load the files in order as they appear in Windows Explorer instead of string extract and such to put them in order ^^
0
 
phoffricCommented:
You can modify cmpString to suit your ordering needs any way you want. However, I made no changes except to add .txt and here is what I get:
BEFORE
---------
TESTDATA1.txt
TESTDATA3.txt
TESTDATA10.txt
TESTDATA1000.txt
TESTDATA1001.txt
TESTDATA2000.txt
TESTDATA40.txt
TESTDATA100.txt
TESTDATA2.txt

But, I have another approach which you may prefer...
AFTER
-------------
TESTDATA1.txt
TESTDATA2.txt
TESTDATA3.txt
TESTDATA10.txt
TESTDATA40.txt
TESTDATA100.txt
TESTDATA1000.txt
TESTDATA1001.txt
TESTDATA2000.txt

Open in new window

0
 
phoffricCommented:
>> as the first method would be less time consuming maybe :)
OK, if you want to insert the filenames in the correct order as you find them rather than doing a sort on a vector after you get them all, here is how to do it. I am assuming that you will not have duplicate filenames.

Instead of vector, use set
http://www.cplusplus.com/reference/stl/set/set/
classcomp is a struct that holds the same logic (cutNpaste) as cmpString had when I used vector.

As you insert the filenames, the set insert operation creates a balanced tree sorted per your instructions in the bool operator() method within classcomp.



If you have duplicate filenames, do not use set.
set<string, classcomp> filenames;
filenames.insert("TESTDATA1.txt"); // no need to sort now!

Open in new window

0
 
mr_stevieAuthor Commented:
For your sort method, what is your cmpString() function?
I'm dealing with CStrings and not std::strings and how you're going about doing so comparing the strings.
0
 
phoffricCommented:
==============================================================================
Dear mr_stevie,
Given the nature of your question would you be kind enough to confirm whether this is an academic related assignment? I see that you are new to EE. I just want to make sure that you are aware of the EE policy on homework.
                   http://www.experts-exchange.com/help.jsp?hi=21
As per the member agreement, experts are only allowed to provide advice and guidence and not full solutions for academic questions so this clarification is just to ensure the experts are pitching their responses correctly.
==============================================================================
Since I don't have MFC I used strings. You can use any type instead of string.

As far as cmpString goes (call it cmpCStrings if that is what you are working with), the logic above still holds, right?
     if the length of x > length of y, then we want x > y
     if the length of x < length of y, then we want x > y
     if the length of x == length of y, then if x.compare(y) < 0, then we want x < y

In the sort link
       http://www.cplusplus.com/reference/algorithm/sort/
are examples of the comparator. Below are some excerpts:
// approach 1: define a comparison function
bool myfunction (int i,int j) { return (i<j); }

// approach 2: define a bool operator within a struct
struct myclass {
  bool operator() (int i,int j) { return (i<j);}
} myobject;

// using approach 1
sort (myvector.begin()+4, myvector.end(), myfunction);

// using approach 2
sort (myvector.begin(), myvector.end(), myobject);

Open in new window

0
 
itsmeandnobodyelseCommented:
>>>> would you be kind enough to confirm whether this is an academic related assignment?
phoffric, the two questions asked by mr_stevie gave IMO no indication for homework ...

mr_stevie, as an alternative to providing a sort function you can derive from CString and make a CFileString. Then provide an operator< function that compares two CFileString objects:

class CFileString : public CString
{
     CFileString(const CString & s) : CString(s) {}
     virtual ~CFileString() {}
public:
     bool operator< (const  CFileString & fs) const
     {
           int pos1 = GetLength();
           int pos2 = fs.GetLength();
           while (pos1 > 0 && isdigit(GetAt(--pos1)));
           while (pos2 > 0 && isdigit(fs.GetAt(--pos2)));
           if (pos1 != pos2 || Left(pos1) != Left(fs.pos2))
               return CString::operator< ((const CString &) fs);
           return atoi((const char*)(const CString &)(*this)) < atoi((const char*)(const CString &)fs);
     }    
};

Then read each filename into a CString but add a CFileString instead of CString to a vector<CFileString>. Finally you could sort by

     std::sort(filearr.begin(), filearr.end());

Note, the above compare function would also work with different prefixes in the filename.

0
 
evilrixSenior Software Engineer (Avast)Commented:
re http:#28509964

Rather than using a home-grown predicate for sort you can just use the one already defined in the <functional> header, in this case std::less.

http://www.cplusplus.com/reference/std/functional/less/
0
 
evilrixSenior Software Engineer (Avast)Commented:
>>  in this case std::less.
Assuming you was ascending ordering of course :)
0
 
phoffricCommented:
Copied cmpString twice. For the set  approach, you needed classcomp defined:
struct classcomp {
   bool operator() (const string& x, const string & y) const{
      if( x.size() < y.size() ||
         (x.size() == y.size() && x.compare(y) < 0 ) ) {
         return true;
      }
      else {
         return false;
      }
   }
};

Open in new window

0
 
mr_stevieAuthor Commented:
Thank you for informing me of the EE policies on homework. Yes, I am a new member however this is not for homework. I am junior programmer doing something for work.

As for as the code goes, it seems that phoffric's is the most simple to implement and there doesn't seem to be many problems as of yet. However, the naming conventions of the filenames is a different format than the example I provided. This may cause problems in the future but for now, I'll be using phoffric's method. Although overloading an operator would be something to consider if problems should arise :)
0
 
phoffricCommented:
>> the naming conventions of the filenames is a different format than the example I provided
If you want to indicate the actual naming conventions, then feel free to clarify. Although I'm not that familiar with MFC, I know there are some excellent MFC experts who would be able to assist you. You can do that in this thread, or hit the ask a related question link if you choose to close this question.
Good luck in your tasks!
0
 
itsmeandnobodyelseCommented:
>>>> it seems that phoffric's is the most simple to implement
>>>> the naming conventions of the filenames is a different format than the example I provided
>>>> Although overloading an operator would be something to consider

You can combine the functionality of the operator I provided with the code phoffric posted (approach 1)

     bool compareString (const  CString & s1, const  CString & s2)
     {
           int pos1 = s1.GetLength();
           int pos2 = s2.GetLength();
           // check for first non-digit from the right
           while (pos1 > 0 && isdigit(s1.GetAt(--pos1)));
           while (pos2 > 0 && isdigit(s2.GetAt(--pos2)));
           // if we have different prefixes we simply make a normal string compare
           if (pos1 != pos2 || s1.Left(pos1).CompareNoCase(s2.Left(pos2)) != 0)
               return (s1.CompareNoCase(s2) < 0);
           // coming here the prefixes were equal and we only compare the numbers
           return atoi((const char*)s1.Mid(pos1)) < atoi((const char*)s2.Mid(pos2));
     }    
     ....

     std::vector<CString> names;
     fillNames(names);
     sort(names.begin(), names.end(), compareString);




0
 
alcindorCommented:
Hello All,
I have a comment to make. The attached code uses an associated array to store the file name strings by "calculated numerical value", by which I mean the numerical part of the file name that prefixes the character that delimits the file name and directory name (folder name).
I have pre-loaded a string vector v, with some test data and tested it.
My code iterates through the string vector v, and populates the associative array u.
The code then iterates through the associative array to show the results.
Code will need to guard against invalid or out of range data
You could dispense with the vecto and use the associated array instead ?

Roger
#include <map.h>
#include <vector.h>
#include <math.h>

typedef vector<string>stringvector;
typedef map<long,string>intstringmap;

stringvector v;
intstringmap m;

// add code here to populate the vector v with file names

    int k,p,i,n=Memo1->Lines->Count;
    long j;
    string s;

    for(i=0,n=v.size();i<n;i++)
    {   // populate map
        s = v[i];    // get next item from vector array
        k = s.find('.',0);  // find the delimiter for the directory name
        // now scan back until a non numeric character is found
        // calculating the numerical value as we go
        for(j=p=0;(s.at(k-1)>='0')&&(s.at(k-1)<='9');k--,p++)
        {
            j = j + (s.at(k-1)-'0') * pow(10,p);
        }
        // now store the file name in an associative array
        //  indexed by numerical value calculated above
        m[j] = s;
    }
    // now display the result by iterating through the associative array
    Memo2->Lines->Clear();
    intstringmap::iterator d;
    for(d=m.begin();d!=m.end();d++)
    {
        Memo2->Lines->Add(d->second.c_str());
    }

Open in new window

0
 
alcindorCommented:
Sorry my reference to u should be m.
0
 
phoffricCommented:
alcindor: Is there a functional difference in output from previous posts?
0
 
alcindorCommented:
No difference in output, if numeric part of the filename is greater than the maximum decimal value represented by a long integer then the definition of  intstringmap should be declared as follows

typedef map<double,string>intstringmap;
and the variable j should be declaredas a double

This will allow for very large numeric values.

Roger

0
All Courses

From novice to tech pro — start learning today.