Solved

strcat issue

Posted on 2004-10-01
43
436 Views
Last Modified: 2010-04-01
Hi expert, I am asking a lot questions. Here is the new one regarding strcat.
I want to strcat the "-" to the third column first, then strcat the second column to the third column.

here is my code, no compiling error, but results are funny. Whats wrong with my code ? Thank you

                                                                         ...
                                                char *token = MyStrTok( row_read,',' );

                  while( token != NULL )
                  {
                              // append "-" to 3rd column
                              if (i == 2)
                              {
                                    char tmp3[500];
                                    strcpy(tmp3,token);
                                    strcat(tmp3,"-");                  // strcat "-" to tmp3
                                    cout << tmp3 << endl;        << i have funny result here
                                    
                              }
                                                                                            // append 2nd column to 3rd column
                               else if (i == 1)
                               {
                                     char tmp2[500];
                                     strcpy (tmp2,token);
                                     strcat (tmp3,tmp2);
                                    cout << tmp3 << endl;      << i have funny result here                        
                                     strcpy (row_elem[3],tmp3);
                               }
                               else
                               {
                                    strcpy(row_elem[i], token );
                                    //strcpy(row_elem[i], tmp2 );
                               }      
                                          //* Get next token: */
                                    token = MyStrTok( NULL, ',' );
                                    i++;
                              
                                    //cout << token << endl;
                  }
      
0
Comment
Question by:justinY
  • 22
  • 21
43 Comments
 
LVL 19

Expert Comment

by:drichards
ID: 12201756
Where is the value of 'i' being set?  Also, your logic seems incorrect.  You need to save the value of column 2 somewhere until you read column 3.  Then you can addpend the '-' and the column 2 string to column 3 when you read column 3.  I assume 'i' is the 0-based column number?
0
 
LVL 19

Expert Comment

by:drichards
ID: 12201832
Also, what is the "funny" result?  The strcat lines are correct, it is the parameters that seem to be the problem.

And what is row_elem?  If you are concatenating columns 2/3 into a single string, it seems that 'i' needs to skip an increment somewhere in there.
0
 

Author Comment

by:justinY
ID: 12201939
Hi drichards, thank you very much for my last question. Your solution works really well. Its hard to see you here. So i am going to use all my day to get as much as i can from you.
Back to this question,
I set the value of 'i' in if ( i ==2 ) and if ( i ==3 ). So, I change my code here:
                           if (i == 1)
                         {
                              char tmp2[500];
                              char tmpsave[500];
                              strcpy(tmp2,token);
                              strcpy(tmpsave,tmp2);          // save tmp2 in tmpsave
                          }
                           else if ( i == 2)
                          {
                              char tmp3[500];
                              strcopy(tmp3,token);
                              strcat(tmp3,"-");
                              strcat(tmp3,tmpsave)         // Is this right ? Thank you
                           }
                             
0
 
LVL 19

Expert Comment

by:drichards
ID: 12202174
You don't need to copy into the temporary tmp2 when i==1.  You need to make sure row_elem gets updated properly as well.  It's not clear what your intention is.

Also, you could use a variation of the GetField method from the other question:

int GetFields(std::string &aStr, std::vector<string> &aVec, char aDelim)
{
    std::istringstream ss(aStr);
    std::string field;
    while (std::getline(ss, field, aDelim))
    {
        aVec.push_back(field);
    }
    return aVec.size();
}

This will fill a vector with all the tokens in one shot.  Then you can just do some simple logic like:

        std::string line(row_read);
        std::vector<string> fields;
        int numFields = GetFields(line, fields, ',');
        fields[2] += "-";
        fields[2] += fields[1];
        std::vector<string>::iterator iter = fields.begin();
        iter++;    // move to second column
        fields.erase(iter); // erase the 2nd column that was appended to column 3

If I read your code correctly, fields should be equivaent to your row_elem array (except that fields is a std::vector of strings).  Or did you mean to hav row_elem contain all the original tokens/fields, in which case you should replace fields[2] += ... with:

    std::string combo = fields[2];
    combo += "-";
    combo += fields[1];

and leave the fields vector alone (no erasing).
0
 

Author Comment

by:justinY
ID: 12202249
This is not working neither.

row_elem[i] is the fields of the row. So baciscally I want to strcat 3rd column to 2nd column. between them I have a '-'. like nnnnnnnn-mmmmmm.

No compiling errors, but when cout << tmp3 << endl; it displays strange character and never stops. it just runs and runs.
Whats wrong ?
0
 
LVL 19

Expert Comment

by:drichards
ID: 12202371
Only problem I see is that tmpsave needs to be declared outside the 'while( token != NULL )' loop so it doesn't go out of scope and lose its value between iterations.  It's giving you a bad strcat call in the i==2 case.  You can also eliminate tmp2 if you haven't already.

I'd still recommend looking at the GetFields function as it gives you row_elem directly.
0
 

Author Comment

by:justinY
ID: 12203143
drichards,
I am more interested in your GetFields function.
But I have compiling errors at

int GetFields(std::string &aStr, std::vector<string> &aVec, char aDelim)
{
    std::istringstream ss(aStr);                                                << cannot convert from 'std::string' to int
    std::string field;
    while (std::getline(ss, field, aDelim))                                   << and errors here
    {
        aVec.push_back(field);
    }
    return aVec.size();
}

I have #include <vector> also.
0
 

Author Comment

by:justinY
ID: 12203186
Hi again, the only reason i use row_elem[i] is that I need to do
fout << setw(10) << row_elem[0]
       << setw(10)<<row_elem[2]
       << setw(10)<<row_elem[3}
         ....
       <<setw(10)<< row_elem[20]
       <<endl;

If GetFields function can do the same thing, I will be more than happy to use it.
                                                                                         
0
 
LVL 19

Expert Comment

by:drichards
ID: 12203189
Need to #include <sstream> as well
0
 
LVL 19

Expert Comment

by:drichards
ID: 12203248
everywhere you see 'row_elem[n]', substitute 'fields[n].c_str()', assuming the vector is called fields.  If you name the vector 'row_elem', just add '.c_str()':

fout << setw(10) << row_elem[0].c_str()  // or 'fields[0].c_str()' if the vector is named 'fields' as in my sample code.
       << setw(10)<<row_elem[2].c_str()
       << setw(10)<<row_elem[3].c_str()
         ....
       <<setw(10)<< row_elem[20].c_str()
       <<endl;
0
 

Author Comment

by:justinY
ID: 12203542
I am using GetFields function to rewrite my program, so I can truely understand it. My main frame is the followings:
my input file
transaction id,notes1,notes2,notes3,account name,amount,trade date,load amount
="111",="aaa",aaaaaaaa3,a4,,100000,1/1/2004,0
="222",="bbb",bbbbbbbb3,b4,,200000,1/15/2004,0
="333",="ccc",cccccccc3,c4,cccc5,300000,1/15/2004,888
="444",="ddd",dddddddd3,d4,dddd5,400000,1/15/2004,-998
="555",="eee",eeeeeeee3,e4,eeee5,500000,1/15/2004,9800
="666",="fff",ffffffff3,f4,ffff5,600000,1/15/2004,0
="777",="ggg",gggggggg3,g4,gggg5,700000,1/15/2004,-7654
="888",="hhh",hhhhhhhh3,h4,hhhh5,800000,1/15/2004,99
="999",="iii",iiiiiiii3,i4,iiii5,900000,1/15/2004,-7877

my code:

#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <sstream>

using namespace std;

int GetFields(std::string &aStr, std::vector<string> &aVec, char aDelim)
{
std::istringstream ss(aStr);
std::string field;
while (std::getline(ss, field, aDelim))
{
aVec.push_back(field);
}
return aVec.size();
};

string removechars( string st, string delim )
{
    string result = "";

    for( unsigned int x = 0; x < st.length(); x++ )
    {
         if( delim.find_first_of( st[x] ) == delim.npos )
             result += st[x];
}
    return result;
};

void DateConvert(char* date)
{
int day, month, year;
if ( sscanf ( date, "%d/%d/%d", &month, &day, &year ) == 3 );
sprintf(date, "%02d%02d%04d", month, day, year );
};

main(int arc, char *arv[])
{
      ifstream fin("fin.txt");
      ofstream fout("fout.txt");
      char row_read[512];
      char row_elem[50][50];
      int i=0;
      string str;
      int day, month, year;
      char date[50];
while (fin.getline(row_read, sizeof(row_read))
{
std::string line(row_read);
std::vector<string> fields; // vector called fileds
int numFields = GetFields(line, fields, ',');
fields[2] += "-";
fields[2] += fields[1];
std::vector<string>::iterator iter = fields.begin();
iter++; // move to second column
fields.erase(iter); // erase the 2nd column that was appended to column 3
cout<<fileds[2].c_str()<<endl;

}
}

I have compiling errors, whats wrong ?
0
 
LVL 19

Expert Comment

by:drichards
ID: 12203636
I fixed the errors and changed the code slightly.  Here are the changes...

void DateConvert(char* date)
{
    int day, month, year;
    if ( sscanf ( date, "%d/%d/%d", &month, &day, &year ) == 3 ); // do you really need 'if ...'.  Gives compiler warning.
    sprintf(date, "%02d%02d%04d", month, day, year );
};

main(int arc, char *arv[])
{
    ifstream fin("D:\\DevProjects\\CPPConsole\\fin.txt");
     ofstream fout("fout.txt");
//     char row_elem[50][50];
     int i=0;
     string row_read; // use std::string
//     int day, month, year;
//     char date[50];

     while (std::getline(fin,row_read)) // use std::getline
    {
    std::vector<string> fields; // vector called fileds
    size_t numFields = GetFields(row_read, fields, ',');
    fields[2] += "-";
    fields[2] += fields[1];
    std::vector<string>::iterator iter = fields.begin();
    iter++; // move to second column
    fields.erase(iter); // erase the 2nd column that was appended to column 3
    cout<<fields[1].c_str()<<endl; // old column 2 was erased.

    }
}
0
 

Author Comment

by:justinY
ID: 12203880
Thanks, It works very well. wow, what a productive day ! I will fill in my stuff to rewrite my code by using GetFields. How late u still here ? I might run into some problems.
0
 
LVL 19

Expert Comment

by:drichards
ID: 12203902
I'll be around for e few hours yet.
0
 

Author Comment

by:justinY
ID: 12204088
I have trouble to plug in my two functions below. I dont know which parameter i need pass into the functions

string removechars( string st, string delim )
{
    string result = "";

    for( unsigned int x = 0; x < st.length(); x++ )
    {
         if( delim.find_first_of( st[x] ) == delim.npos )
             result += st[x];
}
    return result;
};

void DateConvert(char* date)
{
int day, month, year;
if ( sscanf ( date, "%d/%d/%d", &month, &day, &year ) == 3 );
sprintf(date, "%02d%02d%04d", month, day, year );
};
0
 
LVL 19

Expert Comment

by:drichards
ID: 12204147
In DateConvert and removechars, are you trying to modify the original string or get a new one?  Or do you want the option of either?
0
 

Author Comment

by:justinY
ID: 12204203
I want both, and I am trying to modify the original string, after that my string are clean, then i can do MyStrTok, fields and cout <<
0
 

Author Comment

by:justinY
ID: 12204220
we are using fileds to replace row_elem, so are we using line to replace row_read ?
0
 
LVL 19

Expert Comment

by:drichards
ID: 12204254
>> we are using fileds to replace row_elem, so are we using line to replace row_read
Yes.  You can always rename fields and line back to row_elem and row_read so it looks more the same.

And here's 'removechars'.  It returns a new string.  You can replace the original string like this:  'original = removechars(original, "/0");'.  If original was "1/1/2004", the result would be "1124" as the '/' and '0' characters would be removed from the string.  I'm looking at DateConvert.

----------------------------------------------------------------------
string removechars( string &st, string delim )
{
    string result = "";

    size_t npos1 = 0, npos2 = 0;
    while ((npos2 = st.find_first_of(delim, npos1)) < st.length())
    {
        result += st.substr(npos1, (npos2-npos1));
        npos1 = npos2 + 1;
    }
    result += st.substr(npos1);
    return result;
};
0
 

Author Comment

by:justinY
ID: 12204269
I am heading home now. I will be here tonight and tomorrow morning. Will you be here tomorrow ? what time? I am in US Eastern Time Zone, where r u?
If I dont see you, have a great weekend ! Thanks again
0
 
LVL 19

Expert Comment

by:drichards
ID: 12204277
I'm Central time, and yes, I'll be hanging around my computer over the weekend.
0
Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

 
LVL 19

Expert Comment

by:drichards
ID: 12204333
Here's a version of DateConvert that modifies the input string (like your original did):
-----------------------------
void DateConvert(string &date)
{
    int day, month, year;
    if ( sscanf ( date.c_str(), "%d/%d/%d", &month, &day, &year ) == 3 )
    {
        char newdate[32];
        sprintf(newdate, "%02d%02d%04d", month, day, year );
        date = newdate;
    }
};
--------------------------------

Here's one that doesn't:
-------------------------------------------
string DateConvert(string &date)
{
    int day, month, year;
    string result;
    if ( sscanf ( date.c_str(), "%d/%d/%d", &month, &day, &year ) == 3 )
    {
        char newdate[32];
        sprintf(newdate, "%02d%02d%04d", month, day, year );
        result = newdate;
    }
    else
    {
        result = date;
    }
    return result;
};
0
 

Author Comment

by:justinY
ID: 12207158
I plug GetFields function into my program.  and i have compiling error : 'GetFields' : cannot convert parameter 1 from 'char [512]' to 'std::string &'
Whats wrong ?

my purposes in this program are:

1.skip first line
2. remove unwanted characters
3. convert date format
4. strcat row_elem[1] to row_elem[2]
5. extract substring from row_elem[4] and chop it into 3 pieces by positions(e.g. substring1 position 1-2, substring2 position 2-3, substring3 position 3-4)

main(int arc, char *arv[])
{

      ifstream fin("fin.txt");
      ofstream fout("fout.txt");
      char row_read[512];
      char row_elem[50][50];

      ifstream fin2("frefer.csv");
      ofstream fout2("refer_out.txt");
      KeyRef refer_data[MaxSize];
      char refer_read[MaxSize];
      char refer_row_elem[2][MaxSize];
      int i=0,j=0,k=0;

      int isFirstLine = 0;

///////////////////////////////////////////////////////////////////
// for each line of file  
// assume there are 7 columns in each row in file fin.txt
///////////////////////////////////////////////////////////////////

while( fin.getline( row_read, sizeof( row_read ) ) )
{
      //////////////////////////////////////////
      // skip first line
      //////////////////////////////////////////
      if( isFirstLine == 0)
      {
                  //isFirstLine = false;
                  isFirstLine = 1;
                  continue ;
      }

      //////////////////////////////////////////
      // get tokens, parse string with ","
      //////////////////////////////////////////
      char *token = MyStrTok( row_read, ',' );

      while( token != NULL )
      {

                                 ///////////////////////////////////////////////////////////
            // strim '"', '=' etc.. in the fields
            //////////////////////////////////////////////////////////
             char intrBuf[MaxSize];
             strcpy(intrBuf,token);
             removeChar(intrBuf,'=');      
             removeChar(intrBuf,'"');

            /////////////////////////////////////////////////////////////
            //write the tokens into row_elem[i]
            ////////////////////////////////////////////////////////////
            //cout << i <<  " after strim <" << intrBuf << ">" <<  endl;

            ////////////////////////////////////
            // skip blank column
            ////////////////////////////////////
            if (token[0] == '\0') {
                  //cout << "Blank field found - " << i << endl;
                         strcpy(row_elem[i], " ");
            }
            else {
            
                  ////////////////////////////////
                  // convert date field
                  ////////////////////////////////
                  if (i == 9) {
                              DateConvert(intrBuf);
                               strcpy(row_elem[i], intrBuf);
                  }
                  else
                               strcpy(row_elem[i], intrBuf );

//////////////////////////////////////////////////////////////////////////////////
// strcat row_elem[1] to row_elem[2]
// plug in GetFields function here, and compiling errors
/////////////////////////////////////////////////////////////////////////////////

std::vector<string> row_elem; // vector called fileds
    size_t numFields = GetFields(row_read, row_elem, ',');
    row_elem[2] += "-";
    row_elem[2] += row_elem[1];
    std::vector<string>::iterator iter = row_elem.begin();
    iter++;
//cout<<row_elem[2].c_str()<<endl;


            }

                   /* Get next token: */
                   token = MyStrTok( NULL, ',' );
                   i++;
       } //end of while
}
0
 
LVL 19

Expert Comment

by:drichards
ID: 12207184
A couple of things:

1) You don't need MyStrTok any more - it is replaced by GetFields which does the whole job at once.

2) I made a subtle change to GetFields to take a string& instead of a string as first parameter.  This means you need to pass a string, not a char*.  If you use std::getline to read the file (since you don't need to use MyStrTok any more this is OK.

I'll show you what I mean - give me a few minutes.
0
 
LVL 19

Expert Comment

by:drichards
ID: 12207349
One question for you.  You have logivc in your while (token) loop that says 'if (i == 9)' yet you never set i back to 0 at any point.  Are you really stuffing tokens into row_elem in a big long string, or is row_elem just based on a single row of the file and i should be reset for each new line?
0
 
LVL 19

Expert Comment

by:drichards
ID: 12207357
OK, another question.  Does the combined col3/col2 string go into row_elem or do the two strings go in separately?
0
 
LVL 19

Expert Comment

by:drichards
ID: 12207426
OK, here's a version of your code that assumes row_elem is an array for each line only.  It can easily modified to make row_elem running storage for all the tokens.  It's not clear what you're doing with row_elem in the end.  Also, this version does NOT append col2 to col3 because that was another question I had.  Also, with the 'if (ii==9)', the comment at the top of the program says there are 7 columns, so how can ii ever be 9?
----------------------------------------------------------------------------------
string removechars( string &st, string delim )
{
    string result = "";

    size_t npos1 = 0, npos2 = 0;
    while ((npos2 = st.find_first_of(delim, npos1)) < st.length())
    {
        result += st.substr(npos1, (npos2-npos1));
        npos1 = npos2 + 1;
    }
    result += st.substr(npos1);
    return result;
};

void DateConvert(string &date)
{
    int day, month, year;
    if ( sscanf ( date.c_str(), "%d/%d/%d", &month, &day, &year ) == 3 )
    {
        char newdate[32];
        sprintf(newdate, "%02d%02d%04d", month, day, year );
        date = newdate;
    }
};

main(int arc, char *arv[])
{
    ifstream fin("fin.txt");
    ofstream fout("fout.txt");
    string row_read;

    ifstream fin2("frefer.csv");
    ofstream fout2("refer_out.txt");
//    KeyRef refer_data[MaxSize];
//    char refer_read[MaxSize];
//    char refer_row_elem[2][MaxSize];
//    int j=0,k=0;

///////////////////////////////////////////////////////////////////
// for each line of file  
// assume there are 7 columns in each row in file fin.txt
///////////////////////////////////////////////////////////////////
//////////////////////////////////////////
// skip first line
//////////////////////////////////////////
    std::getline(fin, row_read);

    while( std::getline(fin, row_read) )
    {

        //////////////////////////////////////////
        // get tokens into row_elem, parse string with ","
        //////////////////////////////////////////
        vector<string> row_elem;
        GetFields(row_read, row_elem, ',');

        for ( unsigned ii = 0; ii < row_elem.size(); ii++ )
        {
            string &curStr = row_elem[ii];
            ///////////////////////////////////////////////////////////
            // remove '"', '=' in the fields
            //////////////////////////////////////////////////////////
            curStr = removechars(curStr, "\"=");

            ////////////////////////////////////
            // put space in empty columns
            ////////////////////////////////////
            if (curStr.empty()) {
                curStr = " ";
            }

            ////////////////////////////////
            // convert date field
            ////////////////////////////////
            if (ii == 9)                             // Only 7 columns according to comment at start?????
            {
                DateConvert(curStr);
            }
        } //end of while
    }
}
0
 

Author Comment

by:justinY
ID: 12207527
answer your questions:
1. row_elem is based on a single row of the file. It reads row by row
2. combined col3/col2 go into row_elem, so i can do fout<<setw(10)<<row_elem[3]

Also, let me give you my whole code. thats my whole picture

input file
transaction id,notes1,notes2,notes3,account name,amount,trade date,load amount
="111",="aaa",aaaaaaaa3,a4,,100000,1/1/2004,0
="222",="bbb",bbbbbbbb3,b4,,200000,1/15/2004,0
="333",="ccc",cccccccc3,c4,cccc5,300000,1/15/2004,888
="444",="ddd",dddddddd3,d4,dddd5,400000,1/15/2004,-998
="555",="eee",eeeeeeee3,e4,eeee5,500000,1/15/2004,9800
="666",="fff",ffffffff3,f4,ffff5,600000,1/15/2004,0
="777",="ggg",gggggggg3,g4,gggg5,700000,1/15/2004,-7654
="888",="hhh",hhhhhhhh3,h4,hhhh5,800000,1/15/2004,99
="999",="iii",iiiiiiii3,i4,iiii5,900000,1/15/2004,-7877


frefer file
111,111aaa
222,222bbb
333,333ccc
444,444ddd
555,555eee
666,666fff
777,777ggg
888,888hhh
999,999iii

my purposes in this program are:

0.I have 2 files: fin and frefer. frefer is a reference file. i need return a frefer column
1.skip first line
2. remove unwanted characters
3. convert date format
4. strcat row_elem[1] to row_elem[2]
5. extract substring from row_elem[4] and chop it into 3 pieces by positions(e.g. substring1 position 1-2, substring2 position 2-3, substring3 position 3-4)


#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <stdlib.h>
#include <stdio.h>
#include <vector>
#include <sstream>


using namespace std;

const int MaxSize=1024;


int GetFields(std::string &aStr, std::vector<string> &aVec, char aDelim)
{
std::istringstream ss(aStr);
std::string field;
while (std::getline(ss, field, aDelim))
{
aVec.push_back(field);
}
return aVec.size();
};

///////////////////////////////////////////////////////////////////////
// convert date formate from mm/dd/yyyy to mmddyyyy
//////////////////////////////////////////////////////////////////////
void DateConvert(char* date)
{
int day, month, year;
//if ( sscanf ( date, "%d/%d/%d", &month, &day, &year ) == 3 );
sscanf ( date, "%d/%d/%d", &month, &day, &year ) == 3 ;
sprintf(date, "%02d%02d%04d", month, day, year );
};

//////////////////////////////////
// struct for reference data
//////////////////////////////////
typedef struct
{             char keyData[100];
            char valueData[100];
}KeyRef;


//////////////////////////////////////////
// function to lookup value by key
//////////////////////////////////////////
char* findKeyValue(KeyRef* ref, char* k){
   
     for (int i=0;i<MaxSize;i++){
          if (!strcmp(ref[i].keyData,k))
               return ref[i].valueData;
     }
    return "None";
            
};

//////////////////////////////////////////
// Remove delimiter  
//////////////////////////////////////////
void removeChar(char* src, char delim ) {
      int i=0,j=0;
      char buf[MaxSize];
      while (src[i] != '\0' ) {
            if (src[i] == delim){
                  i++;
                  continue;
            }
            buf[j++]=src[i];
            i++;
      }
      buf[j] = '\0';
      strcpy(src,buf);
};


////////////////////////////////////////////////////////////
// Custom version of strtok(), fixing empty field problem
////////////////////////////////////////////////////////////
char* MyStrTok(char* text, const char del)
{
    static char *curTok = NULL;
    char *result = NULL;
    if ( text != NULL )
    {
        curTok = text;
    }
    result = curTok;
    if ( curTok != NULL )
    {
        //curTok = ::strchr(curTok, del);
        curTok = strchr(curTok, del);

        if ( curTok != NULL ) *curTok++ = 0;
    }
    return result;
};


///////////////////////////////
//
// main
//
///////////////////////////////
main(int arc, char *arv[])
{

      ifstream fin("fin.txt");
      ofstream fout("fout.txt");
      char row_read[512];
      char row_elem[50][50];

      ifstream fin2("frefer.csv");
      ofstream fout2("refer_out.txt");
      KeyRef refer_data[MaxSize];
      char refer_read[MaxSize];
      char refer_row_elem[2][MaxSize];
      int i=0,j=0,k=0;

      int isFirstLine = 0;

//////////////////////////////////////////
// read refer file into struct of array
//////////////////////////////////////////
while( fin2.getline( refer_read,MaxSize ))
{
                 /////////////////////////////////////////////////
      // skip first line of reference file " frefer
      /////////////////////////////////////////////////
     if( isFirstLine == 0)
     {
            isFirstLine = 1;
     }
     else {

       char *tok = MyStrTok(refer_read,',');
           while( tok != NULL) {
          strcpy(refer_row_elem[j],tok);

          tok= MyStrTok(NULL,',');
          j++;
           }
           strcpy(refer_data[k].keyData, refer_row_elem[0]);
           strcpy(refer_data[k].valueData, refer_row_elem[1]);
           j=0;
           k++;
     }
      
}

/////////////////////////////////////////////////
// output refer file , review data only
/////////////////////////////////////////////////
//for (int q=0;q<k;q++){
//      fout2 << setw(10) << refer_data[q].keyData << setw(10) << refer_data[q].valueData << endl;
//}


///////////////////////////////////////////////////////////////////
// for each line of file  
// assume there are 5 columns in each row in file fin.txt
///////////////////////////////////////////////////////////////////
isFirstLine=0;

while( fin.getline( row_read, sizeof( row_read ) ) )
{
      //////////////////////////////////////////
      // skip first line
      //////////////////////////////////////////
      if( isFirstLine == 0)
      {
                  //isFirstLine = false;
                  isFirstLine = 1;
                  continue ;
      }

      //////////////////////////////////////////
      // get tokens, parse string with ","
      //////////////////////////////////////////
      char *token = MyStrTok( row_read, ',' );

      while( token != NULL )
      {

        ///////////////////////////////////////////////////////////
            // strim '"', '=' etc.. in the fields
            //////////////////////////////////////////////////////////
             char intrBuf[MaxSize];
             strcpy(intrBuf,token);
             removeChar(intrBuf,'=');      
             removeChar(intrBuf,'"');

            /////////////////////////////////////////////////////////////
            //write the tokens into row_elem[i]
            ////////////////////////////////////////////////////////////
            //cout << i <<  " after strim <" << intrBuf << ">" <<  endl;

            ////////////////////////////////////
            // skip blank column
            ////////////////////////////////////
            if (token[0] == '\0') {
                  //cout << "Blank field found - " << i << endl;
                         strcpy(row_elem[i], " ");
            }
            else {
            
                  ////////////////////////////////
                  // convert date field
                  ////////////////////////////////
                  if (i == 9) {
                              DateConvert(intrBuf);
                               strcpy(row_elem[i], intrBuf);
                  }
                  else
                               strcpy(row_elem[i], intrBuf );
                  }

                   /* Get next token: */
                   token = MyStrTok( NULL, ',' );
                   i++;
       } //end of while

     //////////////////////////////////////////////////////////////////////
     // get reference value by looking at key in reference file
     //////////////////////////////////////////////////////////////////////
     char* valueFound;
     valueFound = findKeyValue(refer_data, row_elem[0]);
     //cout << " value found - " << valueFound << endl;

     //////////////////////////////////////////////////////////////////////
     // output to file, first colmn replaced with value from reference file
     //////////////////////////////////////////////////////////////////////

                           fout<<setw(5)      << row_elem[0]
            <<setw(2)            << " "
            <<setw(5)            << row_elem[1]
            <<setw(2)            << " "
            <<setw(5)            << row_elem[2]
            <<setw(2)            << " "
            <<setw(9)            << valueFound
            <<setw(2)            << " "
            <<setw(5)            << row_elem[3]
            <<setw(2)            << " "
            <<setw(10)            << row_elem[4]
            <<setw(2)            << " "
            <<setw(10)            << row_elem[5]
            <<setw(2)            << " "
            <<setw(10)            << row_elem[6]
            <<setw(2)            << " "
            <<setw(10)            << row_elem[7]
            <<setw(2)            << " "
            <<setw(1)            << "S"
            << endl;

            
     i=0;

}

//cout << "End." << endl;
return 0;

}
0
 

Author Comment

by:justinY
ID: 12207541
right, i should be 6 to convert the date format
0
 
LVL 19

Expert Comment

by:drichards
ID: 12207583
OK, based on your answers, you can change the 9 to a 6 and then when the line processing is done (at the bottom of the 'while (std::getline...)' loop) you can combine the columns and remove the extra one from row_elem if you don't want to keep it.  Otherwise, the code I gave you I believe does what you are looking for.
0
 

Author Comment

by:justinY
ID: 12207795
thanks, i am trying to put them together now.


0
 

Author Comment

by:justinY
ID: 12207981
your code working very well.
I am trying to plug my reference part into your code. but i have one compiling error:
 'findKeyValue' : cannot convert parameter 2 from 'const char *' to 'char *' Conversion loses qualifiers

please notice that i still use MyStrTok, just because i dont know how to use GetFields in my reference while loop (the first loop). So I copy my code here for your to correct them. Thanks

using namespace std;

const int MaxSize=1024;


int GetFields(std::string &aStr, std::vector<string> &aVec, char aDelim)
{
std::istringstream ss(aStr);
std::string field;
while (std::getline(ss, field, aDelim))
{
aVec.push_back(field);
}
return aVec.size();
};

string removechars( string &st, string delim )
{
    string result = "";

    size_t npos1 = 0, npos2 = 0;
    while ((npos2 = st.find_first_of(delim, npos1)) < st.length())
    {
        result += st.substr(npos1, (npos2-npos1));
        npos1 = npos2 + 1;
    }
    result += st.substr(npos1);
    return result;
};

void DateConvert(string &date)
{
    int day, month, year;
    if ( sscanf ( date.c_str(), "%d/%d/%d", &month, &day, &year ) == 3 )
    {
        char newdate[32];
        sprintf(newdate, "%02d%02d%04d", month, day, year );
        date = newdate;
    }
};

//////////////////////////////////
// struct for reference data
//////////////////////////////////
typedef struct
{             char keyData[100];
            char valueData[100];
}KeyRef;


//////////////////////////////////////////
// function to lookup value by key
//////////////////////////////////////////
char* findKeyValue(KeyRef* ref, char* k){
   
     for (int i=0;i<MaxSize;i++){
          if (!strcmp(ref[i].keyData,k))
               return ref[i].valueData;
     }
    return "None";
            
};

char* MyStrTok(char* text, const char del)
{
    static char *curTok = NULL;
    char *result = NULL;
    if ( text != NULL )
    {
        curTok = text;
    }
    result = curTok;
    if ( curTok != NULL )
    {
        //curTok = ::strchr(curTok, del);
        curTok = strchr(curTok, del);

        if ( curTok != NULL ) *curTok++ = 0;
    }
    return result;
};

main(int arc, char *arv[])
{
    ifstream fin("fin.txt");
    ofstream fout("fout.txt");
    string row_read;

    ifstream fin2("frefer.csv");
    ofstream fout2("refer_out.txt");
    KeyRef refer_data[MaxSize];
    char refer_read[MaxSize];
    char refer_row_elem[2][MaxSize];
    int j=0,k=0;
      int isFirstLine = 0;

            //////////////////////////////////////////
            // read refer file into struct of array
            //////////////////////////////////////////
while( fin2.getline( refer_read,MaxSize ))
{
    /////////////////////////////////////////////////
      // skip first line of reference file " frefer
      /////////////////////////////////////////////////
     if( isFirstLine == 0)
     {
            isFirstLine = 1;
     }
     else {

       char *tok = MyStrTok(refer_read,',');
           while( tok != NULL) {
          strcpy(refer_row_elem[j],tok);

          tok= MyStrTok(NULL,',');
          j++;
           }
           strcpy(refer_data[k].keyData, refer_row_elem[0]);
           strcpy(refer_data[k].valueData, refer_row_elem[1]);
           j=0;
           k++;
     }
      
}

///////////////////////////// below is drichard's code///////////////////////////
///////////////////////////////////////////////////////////////////
// for each line of file  
// assume there are 7 columns in each row in file fin.txt
///////////////////////////////////////////////////////////////////
//////////////////////////////////////////
// skip first line
//////////////////////////////////////////
    std::getline(fin, row_read);

    while( std::getline(fin, row_read) )
    {

        //////////////////////////////////////////
        // get tokens into row_elem, parse string with ","
        //////////////////////////////////////////
        vector<string> row_elem;
        GetFields(row_read, row_elem, ',');

        for ( unsigned ii = 0; ii < row_elem.size(); ii++ )
        {
            string &curStr = row_elem[ii];
            ///////////////////////////////////////////////////////////
            // remove '"', '=' in the fields
            //////////////////////////////////////////////////////////
            curStr = removechars(curStr, "\"=");

            ////////////////////////////////////
            // put space in empty columns
            ////////////////////////////////////
            if (curStr.empty()) {
                curStr = " ";
            }

            ////////////////////////////////
            // convert date field
            ////////////////////////////////
            if (ii == 6)             // Only 7 columns according to comment at start?????
            {
                DateConvert(curStr);
            }
                  

            } //end of for loop

    row_elem[2] += "-";
    row_elem[2] += row_elem[1];
    std::vector<string>::iterator iter = row_elem.begin();
    iter++; // move to second column
    //row_elem[1].erase(iter); // erase the 2nd column that was appended to column 3
    //cout<<row_elem[2].c_str()<<endl; // old column 2 was erased.

     //////////////////////////////////////////////////////////////////////
     // get reference value by looking at key in reference file
     //////////////////////////////////////////////////////////////////////
     char* valueFound;
     valueFound = findKeyValue(refer_data, row_elem[0].c_str());   /////// I add row_elem[0].c_str() here
                                                                                                /////// and compiling error here
            ////// 'findKeyValue' : cannot convert parameter 2 from 'const char *' to 'char *' Conversion loses qualifiers

     //cout << " value found - " << valueFound << endl;

cout                      << setw(2) << "01"
            << setw(10) << row_elem[1].c_str()
            << setw(10) << row_elem[2].c_str()
            << setw(10) << row_elem[3].c_str()
            << setw(10) << row_elem[4].c_str()
            << setw(10) << row_elem[5].c_str()
            << setw(10) << row_elem[6].c_str()
            << setw(10) << row_elem[7].c_str()
            << setw(1) << "s"
            << endl;
            ii==0;
    }//end of while
return 0;
}
0
 
LVL 19

Expert Comment

by:drichards
ID: 12208105
Here are tha changed parts:

1) changed second param of findKeyValue to const char* to get rid of compile error.
2) Changed first read loop to use GetFields.
----------------------------------------------------------------
//////////////////////////////////////////
// function to lookup value by key
//////////////////////////////////////////
char* findKeyValue(KeyRef* ref, const char* k){
   
     for (int i=0;i<MaxSize;i++){
          if (!strcmp(ref[i].keyData,k))
               return ref[i].valueData;
     }
    return "None";
         
};

main(int arc, char *arv[])
{
    ifstream fin("D:\\DevProjects\\CPPConsole\\fin.txt");
    ofstream fout("D:\\DevProjects\\CPPConsole\\fout.txt");
    string row_read;

    ifstream fin2("D:\\DevProjects\\CPPConsole\\frefer.txt");
    ofstream fout2("D:\\DevProjects\\CPPConsole\\refer_out.txt");
    KeyRef refer_data[MaxSize];
    string refer_read;
    int k=0;

    //////////////////////////////////////////
    // read refer file into struct of array
    //////////////////////////////////////////

    /////////////////////////////////////////////////
    // skip first line of reference file " frefer
    /////////////////////////////////////////////////
    std::getline(fin2, refer_read);
    while(std::getline(fin2, refer_read))
    {
        std::vector<string> refer_row_elem;
        GetFields(refer_read, refer_row_elem, ',');

        strcpy(refer_data[k].keyData, refer_row_elem[0].c_str());
        strcpy(refer_data[k].valueData, refer_row_elem[1].c_str());
        k++;
    }

///////////////////////////// below is drichard's code///////////////////////////
///////////////////////////////////////////////////////////////////
// for each line of file  
// assume there are 7 columns in each row in file fin.txt
///////////////////////////////////////////////////////////////////
//////////////////////////////////////////
// skip first line
//////////////////////////////////////////
    std::getline(fin, row_read);

    while( std::getline(fin, row_read) )
    {

        //////////////////////////////////////////
        // get tokens into row_elem, parse string with ","
        //////////////////////////////////////////
        vector<string> row_elem;
        GetFields(row_read, row_elem, ',');

        for ( unsigned ii = 0; ii < row_elem.size(); ii++ )
        {
            string &curStr = row_elem[ii];
            ///////////////////////////////////////////////////////////
            // remove '"', '=' in the fields
            //////////////////////////////////////////////////////////
            curStr = removechars(curStr, "\"=");

            ////////////////////////////////////
            // put space in empty columns
            ////////////////////////////////////
            if (curStr.empty()) {
                curStr = " ";
            }

            ////////////////////////////////
            // convert date field
            ////////////////////////////////
            if (ii == 6)             // Only 7 columns according to comment at start?????
            {
                DateConvert(curStr);
            }
               

          } //end of for loop

        row_elem[2] += "-";
        row_elem[2] += row_elem[1];
        std::vector<string>::iterator iter = row_elem.begin();
        iter++; // move to second column
        //row_elem[1].erase(iter); // erase the 2nd column that was appended to column 3
        //cout<<row_elem[2].c_str()<<endl; // old column 2 was erased.

     //////////////////////////////////////////////////////////////////////
     // get reference value by looking at key in reference file
     //////////////////////////////////////////////////////////////////////
     char* valueFound;
     valueFound = findKeyValue(refer_data, row_elem[0].c_str());   /////// I add row_elem[0].c_str() here
                                                                                                /////// and compiling error here
            ////// 'findKeyValue' : cannot convert parameter 2 from 'const char *' to 'char *' Conversion loses qualifiers

     //cout << " value found - " << valueFound << endl;

    cout  << setw(2) << "01"
          << setw(10) << row_elem[1].c_str()
          << setw(10) << row_elem[2].c_str()
          << setw(10) << row_elem[3].c_str()
          << setw(10) << row_elem[4].c_str()
          << setw(10) << row_elem[5].c_str()
          << setw(10) << row_elem[6].c_str()
          << setw(10) << row_elem[7].c_str()
          << setw(1) << "s"
          << endl;
    }//end of while
return 0;
}
0
 
LVL 19

Expert Comment

by:drichards
ID: 12208209
And lastly, here's one that gets rid of MaxSize and the other char arrays - modifies findKeyValue and the KeyRef struct.  I'll be out for a while, so if you can save up other questions for later this afternoon.
-------------------------------------------------------------------------------------------------
//////////////////////////////////
// struct for reference data
//////////////////////////////////
 struct KeyRef
{
    string keyData;
    string valueData;
};

//////////////////////////////////////////
// function to lookup value by key
//////////////////////////////////////////
string findKeyValue(vector<KeyRef> &ref, string &k){
   
     for (unsigned i=0;i<ref.size();i++){
          if (ref[i].keyData == k)
               return ref[i].valueData;
     }
    return "None";
         
};

main(int arc, char *arv[])
{
    ifstream fin("fin.txt");
    ofstream fout("fout.txt");
    string row_read;

    ifstream fin2("frefer.csv");
    ofstream fout2("refer_out.txt");
    std::vector<KeyRef> refer_data;
    refer_data.reserve(32); // Make some space to avoid lots of reallocation
    string refer_read;
    int k=0;

    //////////////////////////////////////////
    // read refer file into struct of array
    //////////////////////////////////////////

    /////////////////////////////////////////////////
    // skip first line of reference file " frefer
    /////////////////////////////////////////////////
    std::getline(fin2, refer_read);
    while(std::getline(fin2, refer_read))
    {
        std::vector<string> refer_row_elem;
        GetFields(refer_read, refer_row_elem, ',');

        refer_data.push_back(KeyRef());
        KeyRef &ref = refer_data[refer_data.size()-1];
        ref.keyData = refer_row_elem[0];
        ref.valueData = refer_row_elem[1];
        k++;
    }

///////////////////////////// below is drichard's code///////////////////////////
///////////////////////////////////////////////////////////////////
// for each line of file  
// assume there are 7 columns in each row in file fin.txt
///////////////////////////////////////////////////////////////////
//////////////////////////////////////////
// skip first line
//////////////////////////////////////////
    std::getline(fin, row_read);

    while( std::getline(fin, row_read) )
    {

        //////////////////////////////////////////
        // get tokens into row_elem, parse string with ","
        //////////////////////////////////////////
        vector<string> row_elem;
        GetFields(row_read, row_elem, ',');

        for ( unsigned ii = 0; ii < row_elem.size(); ii++ )
        {
            string &curStr = row_elem[ii];
            ///////////////////////////////////////////////////////////
            // remove '"', '=' in the fields
            //////////////////////////////////////////////////////////
            curStr = removechars(curStr, "\"=");

            ////////////////////////////////////
            // put space in empty columns
            ////////////////////////////////////
            if (curStr.empty()) {
                curStr = " ";
            }

            ////////////////////////////////
            // convert date field
            ////////////////////////////////
            if (ii == 6)             // Only 7 columns according to comment at start?????
            {
                DateConvert(curStr);
            }
               

          } //end of for loop

        row_elem[2] += "-";
        row_elem[2] += row_elem[1];
        std::vector<string>::iterator iter = row_elem.begin();
        iter++; // move to second column
        //row_elem[1].erase(iter); // erase the 2nd column that was appended to column 3
        //cout<<row_elem[2].c_str()<<endl; // old column 2 was erased.

     //////////////////////////////////////////////////////////////////////
     // get reference value by looking at key in reference file
     //////////////////////////////////////////////////////////////////////
    string valueFound = findKeyValue(refer_data, row_elem[0]);

    cout  << setw(2) << "01"
          << setw(10) << row_elem[1].c_str()
          << setw(10) << row_elem[2].c_str()
          << setw(10) << row_elem[3].c_str()
          << setw(10) << row_elem[4].c_str()
          << setw(10) << row_elem[5].c_str()
          << setw(10) << row_elem[6].c_str()
          << setw(10) << row_elem[7].c_str()
          << setw(1) << "s"
          << endl;
    }//end of while
return 0;
}
0
 

Author Comment

by:justinY
ID: 12208316
Thanks alot. your code is really neat
Now I am working on my last problem which is chopping row_elem[4] into 3 segments.
the first segment is cc,dd,ee,ff,gg,hh (from position1-2), the second segment is cc,dd,ee,ff,gg,hh (from position 3-4) and
the third segment is 5,5,5,5,5. so the output is
 
01       aaa    a3-aaa        a4                           100000  01012004         0    s
01       bbb    b3-bbb        b4                           200000  01152004         0    s
01       ccc    c3-ccc         c4     cc   cc        5    300000  01152004       977   s
01       ddd    d3-ddd        d4    dd   dd       5    400000  01152004     -9876  s
01       eee    e3-eee        e4    ee   ee       5    500000  01152004         0     s
01       fff      f3-fff           f4     ff     ff        5    600000  01152004     96543  s
01       ggg    g3-ggg        g4    gg   gg       5    700000  01152004    -99898  s
01       hhh    h3-hhh        h4    hh   hh       5    800000  01152004    -98656  s
01       iii       i3-iii            i4     ii      ii        5    900000  01152004         0     s

How can I do this ?
0
 

Author Comment

by:justinY
ID: 12208491
I have a function:
void substr(const char* src, int start, int len, char ** dest)
{
while ( *src && --start > 0)
src++;

while ( *src && len > 0)
{
**dest = *src;
*dest++;
src++;
len--;
}
**dest = 0x00;
}


so I do these:

substr ( row_elem[4].c_str(), 1, 2, row_elem[40].c_str());
substr ( row_elem[4].c_str(), 3, 2, row_elem[41].c_str());
substr ( row_elem[4].c_str(), 5, 1, row_elem[42].c_str());

I substr row_elem[4] 3 times, and write them to temperary location which are row_elem[40], [41] ,and [42]
then I write to file
but, I get compiling errors. Whats wrong ?
0
 
LVL 19

Expert Comment

by:drichards
ID: 12209231
You cannot use c_str() to get an editable character array.  You can take substrings like this:

if ( row_elem[4].length() > 4 )
{
   /* string part1 = */ row_elem[4].substr(0,2);
   /* string part2 = */ row_elem[4].substr(2,2);
   /* string part3 = */ row_elem[4].substr(4,1);
}

If you want to insert them into row_elem, you can do that using row_elem.insert, but you need to get an iterator to the insert position (similar to the erase that was in an earlier sample).  Otherwise, you can just get the parts as shown.
0
 
LVL 19

Expert Comment

by:drichards
ID: 12209264
If you want to add then to the end of row_elem, you need to use push_back:

    row_elem.push_back(row_elem[4].substr(0,2));
    row_elem.push_back(row_elem[4].substr(2,2));
    row_elem.push_back(row_elem[4].substr(4,2));

Now the new elements will be at positions 8, 9, and 10 since the original had 0 to 7.  You'd be better off just making a custom output function that did:

    cout  << ... << row_elem[4].substr(0,2).c_str() << ... << row_elem[4].substr(2,2).c_str() <<  ... etc.

unless you find it useful to store the temporaries for some reason.
0
 

Author Comment

by:justinY
ID: 12209354
when I do this:
                           cout<< setw(10) << part1
             << setw(10) << part2
             << setw(10) << part3
             << endl;
It displays what I want on the screen.

But when I do this:
                            fout<< setw(10) << part1
             << setw(10) << part2                         <<       I have compiling errors
             << setw(10) << part3
                                 <<endl;
or do this:
                           fout<< setw(10) << row_elem[4].substr(0,2)
            << setw(10) << row_elem[4].substr(2,2)                 <<            I have run time error
            << setw(10) << row_elem[4].substr(4,1)
                                << endl;
0
 

Author Comment

by:justinY
ID: 12209393
also, look my input file
="111",="aaa",a3,a4,,100000,01/01/2004,0
="222",="bbb",b3,b4,,200000,01/15/2004,0
="333",="ccc",c3,c4,cccc555,300000,01/15/2004,977
="444",="ddd",d3,d4,dddd555,400000,01/15/2004,-9876
="555",="eee",e3,e4,eeee555,500000,01/15/2004,0
="666",="fff",f3,f4,ffff555,600000,01/15/2004,96543
="777",="ggg",g3,g4,gggg555,700000,01/15/2004,-99898
="888",="hhh",h3,h4,hhhh555,800000,01/15/2004,-98656
="999",="iii",i3,i4,iiii555,900000,01/15/2004,0

in the first 2 rows of row_elem[4], they are empty, so it should be displayed like this:
            <<      reserve 2 empty spaces here
            <<
cc cc 5
dd dd 5
ee ee 5
ff ff 5
gg gg 5
hh hh 5
ii ii 5




0
 
LVL 19

Accepted Solution

by:
drichards earned 125 total points
ID: 12209463
The runtime errors on the substr calls are in the lines that have an empty field.  You can do substr(0,n) on an empty string because there's always the null termination.  If index is >0 it fails.  The other errors are because you need to do 'part1.c_str()' to an output stream.  As for leaving the empty spaces when the field value is blank, you could use code like this:

string part1;
string part2;
string part3;
if ( row_elem[4].length() > 4 )
{
   part1 = row_elem[4].substr(0,2);
   part2 = row_elem[4].substr(2,2);
   part3 = row_elem[4].substr(4,1);
}

Then:

      fout<< setw(10) << part1.c_str()
           << setw(10) << part2.c_str()
           << setw(10) << part3.c_str()
            <<endl;

Remember, you cannot use a std::string in place of char* (really const char*).  You need to use string.c_str() to get a const char* that will work with the streams.
0
 

Author Comment

by:justinY
ID: 12209805
It works. Thank you very very much for your help. Its my fortune meeting you here.
YOU ARE THE BEST.
0
 

Author Comment

by:justinY
ID: 12231134
How can I add more points ?
I think these question and answers deserve more than 125. Thanks
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Article by: SunnyDark
This article's goal is to present you with an easy to use XML wrapper for C++ and also present some interesting techniques that you might use with MS C++. The reason I built this class is to ease the pain of using XML files with C++, since there is…
  Included as part of the C++ Standard Template Library (STL) is a collection of generic containers. Each of these containers serves a different purpose and has different pros and cons. It is often difficult to decide which container to use and …
The viewer will learn how to pass data into a function in C++. This is one step further in using functions. Instead of only printing text onto the console, the function will be able to perform calculations with argumentents given by the user.
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

706 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now