5

In Java, strings have a charAt() function.

In C++, that function is simply stringname[INDEX]

However, what if I wanted to use a particular number at a certain index of an integer?

E.g.

int value = 9123;

Let's say I wanted to work with the index 0, which is just the 9.

Is there a way to use index at's with integers?

Mysticial
  • 464,885
  • 45
  • 335
  • 332
Edge
  • 2,456
  • 6
  • 32
  • 57

10 Answers10

15
int value = 9123;
std::stringstream tmp;
tmp << value;
char digit = (tmp.str())[0];
Andreas Brinck
  • 51,293
  • 14
  • 84
  • 114
  • 3
    Or more succinctly `char digit=(std::stringstream()< – Michael Anderson Apr 10 '12 at 07:00
  • 1
    Ugh, the answer from Emilio Garavaglia reminded me of something that is worth bringing up here: using `std::stringstream` for conversion operators is not thread safe due to it accessing the global locale. – Michael Anderson Apr 10 '12 at 07:50
  • 1
    @MichaelAnderson: brevity should not hinder readability. If you want a concise version, instead of using a quite obscure one-liner, encapsulate the conversion in a function with a comprehensible name (or use an existing one, such as `boost::lexical_cast` or `std::to_string`). – Luc Touraille Apr 10 '12 at 08:29
  • 1
    @MichaelAnderson Could you elaborate? `std::ostringstream` uses its own instance of `locale`, not the global. And all of the facets are immutable, and can be accessed from any thread, at any time, without need for synchronization. About the only issue would be if someone changed the global locale while `istringstream` was making the copy. To which 1) the global locale is normally set at the beginning of the program, before threads are started, and never changed afterwards, and 2) an implementation which supports threading will make the copy thread safe. – James Kanze Apr 10 '12 at 09:14
9

No, there is no standard function to extract decimal digits from an integer.

In C++11, there is a function to convert to a string:

std::string string = std::to_string(value);

If you can't use C++11, then you could use a string stream:

std::ostringstream stream;
stream << value;
std::string string = stream.str();

or old-school C formatting:

char buffer[32];  // Make sure it's large enough
snprintf(buffer, sizeof buffer, "%d", value);
std::string string = buffer;

or if you just want one digit, you could extract it arithmetically:

int digits = 0;
for (int temp = value; temp != 0; temp /= 10) {
    ++digits;
}

// This could be replaced by "value /= std::pow(10, digits-index-1)"
// if you don't mind using floating-point arithmetic.
for (int i = digits-index-1; i > 0; --i) {
    value /= 10;
}
int digit = value % 10;

Handling negative numbers in a sensible way is left as an exercise for the reader.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Just out of interest, do you know if std::to_string thread safe? (Unlike using stringstreams). – Michael Anderson Apr 10 '12 at 07:52
  • One problem with your arithmetic code is taht it doesn't handle negative numbers. (Also for a negative number the first character would be '-', which means you cant use a return type of `int`) – Michael Anderson Apr 10 '12 at 07:54
  • @MichaelAnderson: All standard library functions are required to be thread-safe in C++11; see 17.6.5.9 if you want the gory details. – Mike Seymour Apr 10 '12 at 08:09
  • @MichaelAnderson Why do you thing using `std::ostringstream` is not thread safe? – James Kanze Apr 10 '12 at 09:15
8

You can use the following formula (pseudo-code) :

currDigit = (absolute(value) / 10^index) modulo 10; // (where ^ is power-of)
giorashc
  • 13,691
  • 3
  • 35
  • 71
  • 3
    One thing to watch out for though is that this needs tweaking if your number is negative. – Michael Anderson Apr 10 '12 at 07:04
  • 1
    With this, indices 0, 1, 2, and 3 gives you 3, 2, 1, and 9, respectively. Presumably the OP wants it the other way around; namely having indices 0, 1, 2, and 3 giving 9, 1, 2, and 3, respectively. – In silico Apr 10 '12 at 07:04
  • 2
    How is this thinking outside the box? Personally I think converting to a string is a stranger idea... – asveikau Apr 10 '12 at 07:05
  • 2
    It's slightly less straight-forward if you want to count from the most significant digit, as the OP does. – Mike Seymour Apr 10 '12 at 07:06
  • well, it is just a suggestion :). About the indices I do not think it is such a big issue having the first digit to be at index 0 (Which logically is correct). And I think the OP specified index 0 as the MSB is because this is what you get from string using charAt(). When you want the first char from a String you get the most left one cause string starts from the left. Numbers start from the right ... – giorashc Apr 10 '12 at 07:08
  • that is absolutely ingenious – Ethan Apr 11 '13 at 18:47
1

Just to make things complete, you can also use boost::lexical_cast, for more info check out the documentation here.

Basically its just a nice wrapper around the code which can be found at Andreas Brinck answear.

Marek Szanyi
  • 2,348
  • 2
  • 22
  • 28
1

Another solution, which does use 0 for the lestmost digit. digits is used to break down value into individual digits in written order. (i.e. "9347" becomes 9,3,4,7). We then discard the first index values. I.e. to get the 3nd digit, we discard the first two and take the new front.

if (value==0 && index ==0) return 0; // Special case.
if (value <0) { ... } // Unclear what to do with this.
std::list<char> digits;
while (value) {
  digits.push_front(value % 10);
  value /= 10;
}
for(; index > 0 && !digits.empty(); index--) {
  digits.pop_front();
}
if (!digits.empty()) {
  return digits.front();
} else
{
  throw std::invalid_argument("Index too large");
}
MSalters
  • 173,980
  • 10
  • 155
  • 350
0

An integer is not a string and therefor you can not do that. What you need is indeed to convert an integer to string. You can use itoa or have a look here.

Community
  • 1
  • 1
Ivaylo Strandjev
  • 69,226
  • 18
  • 123
  • 176
0

Try sprintf to write the integer out to a string:

http://www.cplusplus.com/reference/clibrary/cstdio/sprintf/

Then you can index into the char array that you've just printed into.

Yaniv
  • 991
  • 6
  • 13
0

Longer version respect to Andreas Brink.

The C++ library is designed so that between "sequences" and "values" there is a "mediator" named "stream", that actually act as a translator from the value to their respecting sequence.

"sequences" is an abstract concept whose concrete implementation are "strings" and "files". "stream" is another abstract concept whose correspondent concrete implementation are "stringstream" and "fstream", that are implemented in term of helper classes "stringbuf" and "filebuf" (both derived form the abstract "streambuf") and from a helper object of "locale" class, containing some "facets".

The cited answer code, works this way:

  1. The tmp object of class stringstream is default-constructed: this will construct also internally a stingbuf and a string, plus a locale referencing the facets of the system global locale (the default one remaps the "classic" or "C" locale)
  2. The operator<< between stream and int function is called: there is one of them, for all the basic types
  3. The "int version" gets the num_put facet from the locale, and a "buffer iterator" from the buffer, and calls the put function passing the format flags of the given stream.
  4. the "put function" actually converts the number into the character sequence thus filling the buffer
  5. When the buffer is full, or when a particular character is inserted or when the str function is called, the buffer content is "sent" (copyed, in this case) to the string, and the string content returned.

This very convoluted process looks complex at first but:

  • Can be completely hidden (resulting in two lines of code)
  • Cam be extended to virtually anything but...
  • It is often kept as a (sort of ) misery in its details in the most of C++ courses and tutorials
Community
  • 1
  • 1
Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63
  • Ah and this reminds me of the biggest downfall of such methods. They're not thread safe due to the accessing of the global locale. Or at least they were not in C++03. – Michael Anderson Apr 10 '12 at 07:48
  • @MichaelAnderson: That's not "the" problem: they're not thread-safe mainly because if more threads insert things into a same stream there is no mechanism to manage the interleaving. The locale is not a big issue, since it is just a collection of interfaces that -if properly implemented- should have no "state". But -unfortunately- there are many "poor implementations" around. – Emilio Garavaglia Apr 10 '12 at 07:55
  • Its not about sharing the same stream. Two otherwise separate threads using separate streams to perform their conversions are not thread safe due to them sharing the global locale. So long as you're not changing the locale in any way you _should_ be safe, but there's no guarantee (that I'm aware of). – Michael Anderson Apr 10 '12 at 07:59
  • @MichaelAnderson: ah ... sorry for the misunderstanding: *should*, in fact become *must* in C++11, being the "stateless locale" a specification requirement (not in c++03). That's why there is a `mbstate_t` that is passed to the facets from the stream (and is not resident in the facets) – Emilio Garavaglia Apr 10 '12 at 08:04
  • @MichaelAnderson Nothing was thread safe if your only reference is C++03. If you're using an implementation which supports multithreading (e.g. g++ 3.0 up, VC++, etc.) then there should be no problem due to the locale. – James Kanze Apr 10 '12 at 09:18
0

I've implemented a variant of giorashc s solution, with all the suggested fixes and issues resolved: Its a bit long but it should be fast if everything is inlined: Most of the code is tests which I've left in for completeness.

#include <iostream>
#include <math.h>

char get_kth_digit( int v, int index)
{
  assert(v>0);
  int mask = pow(10,index);
  return '0'+(v % (mask*10))/mask;
}

int count_digits( int v )
{
  assert(v>0);

  int c=0;
  while(v>0)
  {
    ++c;
    v/=10;
  }

  return c;
}

char get_int_index(int v, int index)
{
  if( v==0 ) return '0';
  if( v <  0 )
  {
    if(index==0) { return '-'; }
    return get_int_index(-v,index-1);
  }

  // get_kth_digit counts the wrong way, so we need to reverse the count
  int digits = count_digits(v);
  return get_kth_digit( v, digits-index-1);
}



template<typename X, typename Y>
void compare(const X & v1, const Y & v2, const char * v1t, const char * v2t, uint32_t line, const char * fname )
{
  if(v1!=v2)
  {
    std::cerr<<fname<<":"<<line<<": Equality test failed "<< v1t  << "("<<v1<<") <> " << v2t <<" ("<<v2<<")"<<std::endl;
  }
}

#define test_eq(X,Y) compare(X,Y,#X,#Y,__LINE__,__FILE__)

int main()
{
  test_eq( 1, count_digits(1) );
  test_eq( 1, count_digits(9) );
  test_eq( 2, count_digits(10) );
  test_eq( 2, count_digits(99) );
  test_eq( 3, count_digits(100) );
  test_eq( 3, count_digits(999) );

  test_eq( '1', get_kth_digit(123,2) );
  test_eq( '2', get_kth_digit(123,1) );
  test_eq( '3', get_kth_digit(123,0) );

  test_eq( '0', get_kth_digit(10,0) );
  test_eq( '1', get_kth_digit(10,1) );

  test_eq( '1', get_int_index(123,0) );
  test_eq( '2', get_int_index(123,1) );
  test_eq( '3', get_int_index(123,2) );

  test_eq( '-', get_int_index(-123,0) );
  test_eq( '1', get_int_index(-123,1) );
  test_eq( '2', get_int_index(-123,2) );
  test_eq( '3', get_int_index(-123,3) );

}
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
0

I would convert it to a string, then index it -- CPP also has the:

str.at(i) 

function similar to Java's.

Another simpler loop in C++11 would be a range based loop --

int i = 0
for(auto s : int_or_str){
    if(i == idx)
        cout << s;
    else
        i++  
}

I guess this isn't easier than the standard for loop -- thought auto may be helpful, not really. I know this is answered, but I prefer simple and familiar answers.

Zach

Zach Oakes
  • 185
  • 14