Re: [ROOT]

From: Christian Holm Christensen (cholm@nbi.dk)
Date: Tue Jul 27 2004 - 03:43:04 MEST


Hi Wolf, 

On Mon, 2004-07-26 at 14:26 -0700, Wolf wrote:
> Hi there,
>  
> I am trying to remove a "," from a number in character format (e.g.
> 1,234) using the following code.

A number of things is wrong with your code.  I'll comment inline. 
 
> char *word1c = new char[20];
> string word1s = word1c;
>  
> if(word1s.find(",")!=string::npos){
>      int m = word1s.find(",");

Here, you do the same expensive operation twice, even though you only
need to do it once: 

  string::size_type m = word1s.find(',');
  if (m != string::npos)

Note, that string::find _does_not_ return an `int' - it returns a
string::size_type - which needn't be an integer or even signed - it
depends entirely on the compiler implementation.  

>      if(m!=-1){ cout<<word1s; }

This check is redundant - you have already established that the iterator
m indeed points to a valid location in the string. 

I good idea is to terminate the print out with something that flushes,
especially if you expect to get a response immediately. 

  cout << word1s << endl;

>      word1s.Remove(m,1);

Whoha, a bit to much ROOT creeping in there.   The member function in
std::string is `erase', not `Remove'

>      word1c = word1s.c_str();
> }
>  
> First of all, when I write out word1s inside the if{} statement, it
> prints me also all the lines which do not contain a ",". Any
> suggestions why this happens?

Lines, what lines are you taking about.  I can only see one string
object. 

OK, I expect that your string object actually contains some lines read
from somewhere (a file, perhaps?), and so you have it containing
something like 

  1,234\n1,234\n1,234\n....\n

(That would explain why you didn't terminate the printout with a flush).
Now, the reason why you'd see everything, is that you print out the
entire string, and before you actually remove the ','. 


>  
> Secondly, I get the following error message:
>  
> Error: Can't call string::Remove(m,1) in current scope ...

As I said, the member function you're looking for is `std::string::
erase'. 

Note, that your code is highly inefficient and not very C++-like.  

First off you could write the removal of the ',' much simpler:

  string word("1,234");
  string::size_type m = word.find(',');
  if(m != string::npos) word.erase(m, 1);

Second, if you are indeed reading some file into a single string, and
then convert the numbers there, you're much better off using C++ very
powerful I/O library - especially locales (look up in your favorit C++
reference what locales is, or read Bjarne Stroustrups Appendix D at
[1]).  

I guess you could be reading some output from some office suite that has
kindly formated numbers nicely.  Now, suppose you're Danish friend would
like to use your program, but he has set up the office suite to write
out using his locale ("da_DK"), then all he needs to do, is to set the
right locale. 

Now, what we need to do, is to use the "en_US" locale which defines
grouping of numbers by three (like 1,234.67):

  
  int main()
  {
    // Open the file `tmp.dat'
    std::ifstream in("tmp.dat");
    // Create the en_US locale 
    std::locale l("en_US");
    // Tell the input file to use the locale we just created. 
    in.imbue(l);
    // Read the numbers in the file, using the regular 
    // `istream::operator>>(double&)'
    do {
      float x;
      in >> x;
      std::cout << x << std::endl;
    } while (!in.eof())
    in.close();
    return 0;
  }
I guess you could be reading some output from some office suite that has
kindly formated numbers nicely.  Now, suppose you're Danish friend would
like to use your program, but he has set up the office suite to write
out using his locale ("da_DK"), then all he needs to do, is to set the
right locale:

  std::locale l("da_DK);

and vola, everything works!


Now, with minimal code (construction of a locale a call to ios_base::
imbue) you have turned your hard-crafted code into die-hard, easy-to-
read, pure-bread-C++. 

Suppose you have the values in some string (and not from a file), like
the one I described above.  Well, surprise - you can use the above code
almost as is:

  #include <sstream>
  ...  // Rest of includes like above 

  int main() 
  { 
    std::string read("1,234\n1,234\n1,234\n");
    std::stringstream in(read);
    std::locale l("en_US");
    in.imbue(l);
    ... // Loop code, _exactly like above 
  }
  
See also the attached file. 

Caveat: This has nothing to do with ROOT - it's plain C++.  CINT may
know of locales, and the like.  However, you can compile the relevant
code to a shared library and load that into CINT. 

Yours,

-- 
 ___  |  Christian Holm Christensen 
  |_| |  -------------------------------------------------------------
    | |  Address: Sankt Hansgade 23, 1. th.  Phone:  (+45) 35 35 96 91
     _|           DK-2200 Copenhagen N       Cell:   (+45) 24 61 85 91
    _|            Denmark                    Office: (+45) 353  25 404
 ____|   Email:   cholm@nbi.dk               Web:    www.nbi.dk/~cholm
 | |

[1] http://www.research.att.com/~bs/3rd_loc0.html





This archive was generated by hypermail 2b29 : Sun Jan 02 2005 - 05:50:09 MET