I'm doing THIS UVa problem, which takes in the following input:
This is fun-
ny! Mr.P and I've never seen
this ice-cream flavour
before.Crazy eh?
#
This is fun-
ny! Mr.P and I've never seen
this ice-cream flavour
before.Crazy eh?
#
and produces this output:
1 1
2 3
3 2
4 3
5 3
6 1
7 1
8 1
1 1
2 3
3 2
4 3
5 3
6 1
7 1
8 1
In the input, # divides the cases. I'm supposed to get the length of each word and count the frequency of each different length (as you see in the output, a word of length 1 occurs once, length 2 occurs three times, 3 occurs twice, and so on).
My problem is this: When reading in cin, before.Crazy is counted as one word, since there is no space dividing them. It should then be as simple as splitting the string on certain punctuation ({".",",","!","?"} for example)...but C++ seems to have no simple way to split the string.
So, my question: How can I split the string and send in each returned string to my function that handles the rest of the problem?
Here's my code:
int main()
{
string input="";
while(cin.peek()!=-1)
{
while(cin >> input && input!="#")
{
lengthFrequency(input);
cout << input << " " << input.length() << endl;
}
if(cin.peek()!=-1) cout << endl;
lengthFrequencies.clear();
}
return 0;
}
lengthFrequency is a map<int,int>.
You can redefine what a stream considers to be a whitespace character using a std::locale with a custom std::ctype<char> facet. Here is corresponding code which doesn't quite do the assignment but demonstrates how to use the facet:
#include <algorithm>
#include <iostream>
#include <locale>
#include <string>
struct ctype
: std::ctype<char>
{
typedef std::ctype<char> base;
static base::mask const* make_table(char const* spaces,
base::mask* table)
{
base::mask const* classic(base::classic_table());
std::copy(classic, classic + base::table_size, table);
for (; *spaces; ++spaces) {
table[int(*spaces)] |= base::space;
}
return table;
}
ctype(char const* spaces)
: base(make_table(spaces, table))
{
}
base::mask table[base::table_size];
};
int main()
{
std::cin.imbue(std::locale(std::locale(), new ctype(".,!?")));
for (std::string s; std::cin >> s; ) {
std::cout << "s='" << s << "'\n";
}
}
Before counting the frequencies, you could parse the input string and replace all the {".",",","!","?"} characters with spaces (or whatever separation character you want to use). Then your existing code should work.
You may want to handle some characters differently. For example, in the case of before.Crazy you would replace the '.' with a space, but for something like 'ny! ' you would remove the '!' altogether because it is already followed by a space.
How about this (using the STL, comparators and functors)?
NOTE: All assumptions and explanations are in the source code itself.
#include <iostream>
#include <string>
#include <vector>
#include <cstdlib>
#include <sstream>
#include <algorithm>
#include <cctype>
#include <utility>
#include <string.h>
bool compare (const std::pair<int, int>& l, const std::pair<int, int>& r) {
return l.first < r.first;
}
//functor/unary predicate:
struct CompareFirst {
CompareFirst(int val) : val_(val) {}
bool operator()(const std::pair<int, int>& p) const {
return (val_ == p.first);
}
private:
int val_;
};
int main() {
char delims[] = ".,!?";
char noise[] ="-'";
//I'm assuming you've read the text from some file, and that information has been stored in a string. Or, the information is a string (like below):
std::string input = "This is fun-\nny, Mr.P and I've never seen\nthis ice-cream flavour\nbefore.Crazy eh?\n#\nThis is fun-\nny! Mr.P and I've never seen\nthis ice-cream flavour\nbefore.Crazy eh?\n#\n";
std::istringstream iss(input);
std::string temp;
//first split the string by #
while(std::getline(iss, temp, '#')) {
//find all the occurences of the hypens as it crosses lines, and remove the newline:
std::string::size_type begin = 0;
while(std::string::npos != (begin = temp.find('-', begin))) {
//look at the character in front of the current hypen and erase it if it's a newline, if it is - remove it
if (temp[begin+1] == '\n') {
temp.erase(begin+1, 1);
}
++begin;
}
//now, erase all the `noise` characters ("'-") as these count as these punctuation count as zero
for (int i = 0; i < strlen(noise); ++i) {
//this replaces all the hyphens and apostrophes with nothing
temp.erase(std::remove(temp.begin(), temp.end(), noise[i]), temp.end());//since hyphens occur across two lines, you need to erase newlines
}//at this point, everything is dandy for complete substitution
//now try to remove any other delim chracters by replacing them with spaces
for (int i = 0; i < strlen(delims); ++i) {
std::replace(temp.begin(), temp.end(), delims[i], ' ');
}
std::vector<std::pair<int, int> > occurences;
//initialize another input stringstream to make use of the whitespace
std::istringstream ss(temp);
//now use the whitespace to tokenize
while (ss >> temp) {
//try to find the token's size in the occurences
std::vector<std::pair<int, int> >::iterator it = std::find_if(occurences.begin(), occurences.end(), CompareFirst(temp.size()));
//if found, increment count by 1
if (it != occurences.end()) {
it->second += 1;//increment the count
}
//this is the first time it has been created. Store value, and a count of 1
else {
occurences.push_back(std::make_pair<int, int>(temp.size(), 1));
}
}
//now sort and output:
std::stable_sort(occurences.begin(), occurences.end(), compare);
for (int i = 0; i < occurences.size(); ++i) {
std::cout << occurences[i].first << " " << occurences[i].second << "\n";
}
std::cout << "\n";
}
return 0;
}
91 lines, and all vanilla C++98.
A rough outline of what I did is:
Since hyphens occur across two lines, find all hyphens and remove any newlines that follow them.
There are characters that don't add to the length of a word such as the legitimate hypenated words and the apostrophe. Find these and erase them as it makes tokenizing easier.
All the other remaining delimiters can now be found and replaced with whitespace. Why? Because we can use the whitespace to our advantage by using streams (whose default action is to skip whitespace).
Create a stream and tokenize the text via whitespace as per the previous.
Store the lengths of the tokens and their occurrences.
Sort the lengths of the tokens, and then output the token length and corresponding occurrences.
REFERENCES:
https://stackoverflow.com/a/5815875/866930
https://stackoverflow.com/a/12008126/866930
Related
This question already has answers here:
How do I iterate over the words of a string?
(84 answers)
Closed 2 years ago.
I am missing the last word of string. this is code I used to store word into array.
string arr[10];
int Add_Count = 0;
string sentence = "I am unable to store last word"
string Words = "";
for (int i = 0; i < sentence.length(); i++)
{
if (Sentence[i] == ' ')
{
arr[Add_Count] = Words;
Words = "";
Add_Count++;
}
else if (isalpha(Sentence[i]))
{
Words = Words + sentence[i];
}
}
Let's print the arr:
for(int i =0; i<10; i++)
{
cout << arr[i] << endl;
}
You are inserting the word found when you see a blank character.
Since the end of the string is not a blank character, the insertion for the last word never happens.
What you can do is:
(1) If the current character is black, skip to the next character.
(2) See the next character of current character.
(2-1) If the next character is blank, insert the accumulated word.
(2-2) If the next character doesn't exist (end of the sentence), insert the accumulated word.
(2-3) If the next character is not blank, accumulate word.
Obviously you lost the last word because when you go to the end the last word is not extracted yet. You can add this line to get the last word
if (Words.length() != 0) {
arr[Add_Count] = Words;
Words = "";
}
Following on from the very good approach by #Casey, but adding the use of std::vector instead of an array, allows you to break a line into as many words as may be included in it. Using the std::stringstream and extracting with >> allows a simple way to tokenize the sentence while ignoring leading, multiple included and trailing whitespace.
For example, you could do:
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
int main (void) {
std::string sentence = " I am unable to store last word ",
word {};
std::stringstream ss (sentence); /* create stringstream from sentence */
std::vector<std::string> words {}; /* vector of strings to hold words */
while (ss >> word) /* read word */
words.push_back(word); /* add word to vector */
/* output original sentence */
std::cout << "sentence: \"" << sentence << "\"\n\n";
for (const auto& w : words) /* output all words in vector */
std::cout << w << '\n';
}
Example Use/Output
$ ./bin/tokenize_sentence_ss
sentence: " I am unable to store last word "
I
am
unable
to
store
last
word
If you need more fine-grained control, you can use std::string::find_first_of and std::string::find_first_not_of with a set of delimiters to work your way through a string finding the first character in a token with std::string::find_first_of and then skipping over delimiters to the start of the next token with std::string::find_first_not_of. That involves a bit more arithmetic, but is a more flexible alternative.
This happens because the last word has no space after it, just add this line after for loop.
arr[Add_Count] = Words;
My version :
#include <algorithm>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
int main() {
std::istringstream iss("I am unable to store last word");
std::vector<std::string> v(std::istream_iterator<std::string>(iss), {});
std::copy(v.begin(), v.end(),
std::ostream_iterator<std::string>(std::cout, "\n"));
}
Sample Run :
I
am
unable
to
store
last
word
If you know you won't have to worry about punctuation, the easiest way to handle it is to throw the string into a istringstream. You can use the extraction operator overload to extract the "words". The extraction operator defaults to splitting on whitespace and automatically terminates at the end of the stream:
#include <algorithm>
#include <sstream>
#include <string>
#include <vector>
std::string sentence = // ... Get the string from cin, a file, or hard-code it here.
std::istringstream ss(sentence);
std::vector<std::string> arr;
arr.reserve(1 + std::count(std::cbegin(sentence), std::cend(sentence), ' '));
std::string word;
while(ss >> word) {
arr.push_back(word);
}
I am trying to compare blocks of four numbers with each other to make a new output file with only the ones that meet that: four digit numbers which have all digits the same.
This is my code for the input file:
int main()
{
ofstream outfile ("text.txt");
outfile << "1111 1212 4444 \n 2222 \n \n 8888 4567" <<endl;
I want to split this in blocks of four like "1111", "1212" and so on to be able to only write the ones that meet the requirement in the new output file. I decided to conver the whole file into an integer vector to be able to compare them.
char digit;
ifstream file("text.txt");
vector <int> digits;
while(file>>digit)
{
digits.push_back(digit - '0');
}
and I suppose that the method that compares them would look something like this:
bool IsValid(vector<int> digits){
for (int i=0; i<digits.size() i++)
{
if(digits[0] == digits[1] == digits[2] == digits [3])
return true;
else
{
return false;
}
}
}
However this would just compare the first block, would you do it differently? or should I keep doing the vector idea.
Hm, all what I have seen is rather complicated.
Obviously you want to check for a pattern in a string. And patterns are usually matched with regular expressions.
This will give you an extremely short solution. Use std::regex. Regular expressions are part of C++ standard library. And they are also easy to use. And for your case you the regex is (\d)\1{3}. So, a digit followed by 3 of the same digits.
Program then boils down to one statement:
#include <sstream>
#include <iostream>
#include <iterator>
#include <algorithm>
#include <string>
#include <regex>
std::istringstream testData{R"(1111 1212 444414 555
2222
8888 4567)"};
int main()
{
std::copy_if(
std::istream_iterator<std::string>(testData),
{},
std::ostream_iterator<std::string>(std::cout,"\n"),
[](const std::string& s){
return std::regex_match(s,std::regex(R"((\d)\1{3})"));
}
);
return 0;
}
Of course you may use any std::fstream instead of the std::istringstream
And of course this is only one of many many possible and maybe not the best solution . . .
I decided to conver the whole file into an integer vector to be able to compare them.
You can then extract ints from the stream directly (file >> int_variable) and check if they are multiples of 1111 or not.
Suggestions in code:
#include <fstream>
#include <iomanip>
#include <iostream>
#include <vector>
bool IsValid(int number) {
// Check that number is in the valid range and that it's a multiple of 1111.
return number >= 0 && number <= 9999 && (number / 1111) * 1111 == number;
}
// A function to process the values in a stream
std::vector<int> process_stream(std::istream& is) {
std::vector<int> digits;
int number;
while(is >> number) {
if(IsValid(number)) // Only save valid numbers
digits.push_back(number);
}
return digits;
}
int main() {
std::vector<int> digits;
// Check that opening the file succeeds before using it
if(std::ifstream file = std::ifstream("text.txt")) {
digits = process_stream(file);
}
// Print the collected int:s
for(int x : digits) {
std::cout << std::setw(4) << std::setfill('0') << x << '\n';
}
}
Another approach is to simply handle each input as a string, and the loop over each character in the string validating that it is a digit and equal to the previous character. If it fails either test, then what was read wasn't an integer with all digits equal.
For example you could do:
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
int main (void) {
int main (void) {
std::string s;
std::stringstream ss { "1 11 1111 foo 2222\nbar 1212\n4444\n8888\n4567\n"
"3433333 a8\n9999999999999999999\n" };
while (ss >> s) { /* read each string */
bool equaldigits = true; /* flags equal digits */
for (size_t i = 1; i < s.length(); i++) /* loop 1 - length */
/* validate previous & current digits & equal */
if (!isdigit(s[i-1]) || !isdigit(s[i]) || s[i-1] != s[i]) {
equaldigits = false; /* if not set flag false */
break; /* break loop */
}
/* handle empty-string or single char case */
if (!s.length() || (s.length() == 1 && !isdigit(s[0])))
equaldigits = false;
if (equaldigits) /* if all digits & equal */
std::cout << s << '\n'; /* output string */
}
}
The std::stringstream above simply provides simulated input for the program.
(note: you can loop with std::string::iterator if you like, or use a range-based for loop and prev char to store the last seen. Here, it's just as easy to iterate over indexes)
Using std::string find_first_not_of
Using existing string functions provides another way. After comparing that the first character is a digit, you can use std::basic_string::find_first_not_of to scan the rest of the string for a character that isn't the same as the first -- if the result isn't std::string::npos, then your string isn't all the same digit.
#include <iostream>
#include <sstream>
#include <string>
#include <cctype>
int main (void) {
std::string s;
std::stringstream ss { "1 11 1111 foo 2222\nbar 1212\n4444\n8888\n4567\n"
"3433333 a8\n9999999999999999999\n" };
while (ss >> s) { /* read each string */
if (!isdigit(s.at(0))) /* 1st char digit? */
continue;
/* if remainder of chars not equal 1st char - not equal digits */
if (s.find_first_not_of(s.at(0)) != std::string::npos)
continue;
std::cout << s << '\n';
}
}
Both approaches product the same output.
Example Use/Output
$ ./bin/intdigitssame
1
11
1111
2222
4444
8888
9999999999999999999
There are many other ways to do this as shown by the other good answers. It's worth understanding each approach.
I have this C++ code(Which I will explain below):
#include <iostream>
#include <string>
#include <vector>
#include <cctype>
using namespace std;
int main()
{
// part 1
cout << "Write many words separated by either newlines or spaces:"<< endl;
string word;
vector<string> v;
while(cin >> word){
if(word == "quit"){
break;
}
else{
v.push_back(word);
}
}
//part 2
for(string x:v){
for(char &j:x){
j = toupper(j);
}
}
//part 3
for(string x:v){
cout << x << endl;
}
return 0;
}
What I am trying to do is get a sequence of strings and convert each character in the string to uppercase and output the strings back.
I want to use vectors for this as I am studying it.
In the part 1, I get strings from the standard input and store them in a string vector. I write "quit" to break out of the loop and begin capitalising the letters in each string.
The problem is with part 2,obviously. What I am trying to do there is this:
1- Get a string as we loop.
2 Once we have a string, get a character in that string and transform it into uppercase.Do this for all the characters.
3-Do this for all the strings.
When I compile it, I get all correct except the strings being capitalised.
I am really confused D:
for(string x:v){
for(char &j:x){
j = toupper(j);
}
}
You take every character out of the string by reference, but you take the string by value. Try
for (string& x : v){
// […]
}
Note that with C++1Z we will be able to use terse range-based for loops, making life a lot easier:
for (x : v) { // Captures by reference automatically
// […]
}
I have a string str ( "1 + 2 = 3" ). I want to obtain the individual numbers of the string in their decimal values( not ASCII ). I have tried atoi and c_str(). But both them require the entire string to consist of only numbers. I am writing my code in C++.
Any help would be great.
My challenge is to evaluate a prefix expression. I am reading from a file where each line contains a prefix expression. My code snippet to tokenize and and store the variables is as shown below. Each line of the file contains numbers and operators(+,-,*) which are separated by a space.
Ex - line = ( * + 2 3 4);
ifstream file;
string line;
file.open(argv[1]);
while(!file.eof())
{
getline(file,line);
if(line.length()==0)
continue;
else
{
vector<int> vec;
string delimiters = " ";
size_t current;
size_t next = -1;
do
{
current = next + 1;
next = line.find_first_of( delimiters, current );
if((line[next] <=57)&&(line[next] >=48))
vec.push_back(atoi((line.substr( current, next - current )).c_str()));
}while (next != string::npos);
cout << vec[0] << endl;
}
}
file.close();
In this case vec[0] prints 50 not 2.
You need to learn to delimit a string. Your delimiting characters would be mathematical operators (ie:
C: creating array of strings from delimited source string
http://www.gnu.org/software/libc/manual/html_node/Finding-Tokens-in-a-String.html
In the case of the second link, you would do something like:
const char delimiters[] = "+-=";
With this knowledge, you can create an array of strings, and call atoi() on each string to get the numeric equivalent. Then you can use the address (array index) of each delimiter to determine which operator is there.
For just things like addition and subtraction, this will be dead simple. If you want order of operations and multiplication, parentheses, etc, your process flow logic will be more complicated.
For a more in-depth example, please see this final link. A simple command-line calculator in C. That should make it crystal clear.
http://stevehanov.ca/blog/index.php?id=26
You will not fall into your if, since your next position will be at a delimiter.
string delimiters = " ";
...
next = line.find_first_of( delimiters, current );
if((line[next] <=57)&&(line[next] >=48))
...
Since your delimiters consist of " ", then line[next] will be a space character.
From the description of your problem, you are missing code that will save away your operators. There is no code to attempt to find the operators.
You don't have to assume ASCII for testing for a digit. You can use is_digit() for example, or you can compare against '9' and '0'.
When you print your vector element, you may be accessing the vector inappropriately, because no item may have ever been inserted into the array.
Don't use fin.eof() to control a loop. That function is only useful after a read has failed.
There are a number of ways to get ints from a std::string, I'm choosing std::stoi() from the C++11 standard in this case.
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <string>
#include <vector>
typedef std::vector<int> ints;
bool is_known_operator(std::string const& token)
{
static char const* tokens[] = {"*", "/", "+", "-"};
return std::find(std::begin(tokens), std::end(tokens), token) != std::end(tokens);
}
ints tokenise(std::string const& line)
{
ints vec;
std::string token;
std::istringstream iss(line);
while (iss >> token)
{
if (is_known_operator(token))
{
std::cout << "Handle operator [" << token << "]" << std::endl;
}
else
{
try
{
auto number = std::stoi(token);
vec.push_back(number);
}
catch (const std::invalid_argument&)
{
std::cerr << "Unexpected item in the bagging area ["
<< token << "]" << std::endl;
}
}
}
return vec;
}
int main(int, const char *argv[])
{
std::ifstream file(argv[1]);
std::string line;
ints vec;
while (std::getline(file, line))
{
vec = tokenise(line);
}
std::cout << "The following " << vec.size() << " numbers were read:\n";
std::copy(vec.begin(), vec.end(), std::ostream_iterator<int>(std::cout, "\n"));
}
I'm trying to read just the integers from a text file structured like this....
ALS 46000
BZK 39850
CAR 38000
//....
using ifstream.
I've considered 2 options.
1) Regex using Boost
2) Creating a throwaway string ( i.e. I read in a word, don't do anything with it, then read in the score ). However, this is a last resort.
Are there any ways to express in C++ that I want the ifstream to only read in text that is an integer? I'm reluctant to use regular expressions if it turns out that there is a much simpler way to accomplish this.
why to make simple things complicated?
whats wrong in this :
ifstream ss("C:\\test.txt");
int score;
string name;
while( ss >> name >> score )
{
// do something with score
}
Edit:
it's in fact possible to work on streams directly with spirit than I suggested previously, with a parser:
+(omit[+(alpha|blank)] >> int_)
and one line of code(except for variable definitions):
void extract_file()
{
std::ifstream f("E:/dd/dd.trunk/sandbox/text.txt");
boost::spirit::istream_iterator it_begin(f), it_end;
// extract all numbers into a vector
std::vector<int> vi;
parse(it_begin, it_end, +(omit[+(alpha|blank)] >> int_), vi);
// print them to verify
std::copy(vi.begin(), vi.end(),
std::ostream_iterator<int>(std::cout, ", " ));
}
you get all numbers into a vector at once with one line, couldn't be simpler.
if you do not mind using boost.spirit2. the parser to get numbers from a line only is
omit[+(alpha|blank)] >> int_
to extract everything is
+(alpha|blank) >> int_
See the whole program below(Test with VC10 Beta 2):
#include <boost/spirit/include/qi.hpp>
#include <iostream>
#include <string>
#include <cstring>
#include <vector>
#include <fstream>
#include <algorithm>
#include <iterator>
using std::cout;
using namespace boost::spirit;
using namespace boost::spirit::qi;
void extract_everything(std::string& line)
{
std::string::iterator it_begin = line.begin();
std::string::iterator it_end = line.end();
std::string s;
int i;
parse(it_begin, it_end, +(alpha|blank)>>int_, s, i);
cout << "string " << s
<< "followed by nubmer " << i
<< std::endl;
}
void extract_number(std::string& line)
{
std::string::iterator it_begin = line.begin();
std::string::iterator it_end = line.end();
int i;
parse(it_begin, it_end, omit[+(alpha|blank)] >> int_, i);
cout << "number only: " << i << std::endl;
}
void extract_line()
{
std::ifstream f("E:/dd/dd.trunk/sandbox/text.txt");
std::string s;
int i;
// iterated file line by line
while(getline(f, s))
{
cout << "parsing " << s << " yields:\n";
extract_number(s); //
extract_everything(s);
}
}
void extract_file()
{
std::ifstream f("E:/dd/dd.trunk/sandbox/text.txt");
boost::spirit::istream_iterator it_begin(f), it_end;
// extract all numbers into a vector
std::vector<int> vi;
parse(it_begin, it_end, +(omit[+(alpha|blank)] >> int_), vi);
// print them to verify
std::copy(vi.begin(), vi.end(),
std::ostream_iterator<int>(std::cout, ", " ));
}
int main(int argc, char * argv[])
{
extract_line();
extract_file();
return 0;
}
outputs:
parsing ALS 46000 yields:
number only: 46000
string ALS followed by nubmer 46000
parsing BZK 39850 yields:
number only: 39850
string BZK followed by nubmer 39850
parsing CAR 38000 yields:
number only: 38000
string CAR followed by nubmer 38000
46000, 39850, 38000,
You can call ignore to have in skip over a specified number of characters.
istr.ignore(4);
You can also tell it to stop at a delimiter. You would still need to know the maximum number of characters the leading string could be, but this would also work for shorter leading strings:
istr.ignore(10, ' ');
You could also write a loop that just reads characters until you see the first digit character:
char c;
while (istr.getchar(c) && !isdigit(c))
{
// do nothing
}
if (istr && isdigit(c))
istr.putback(c);
here goes :P
private static void readFile(String fileName) {
try {
HashMap<String, Integer> map = new HashMap<String, Integer>();
File file = new File(fileName);
Scanner scanner = new Scanner(file).useDelimiter(";");
while (scanner.hasNext()) {
String token = scanner.next();
String[] split = token.split(":");
if (split.length == 2) {
Integer count = map.get(split[0]);
map.put(split[0], count == null ? 1 : count + 1);
System.out.println(split[0] + ":" + split[1]);
} else {
split = token.split("=");
if (split.length == 2) {
Integer count = map.get(split[0]);
map.put(split[0], count == null ? 1 : count + 1);
System.out.println(split[0] + ":" + split[1]);
}
}
}
scanner.close();
System.out.println("Counts:" + map);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
readFile("test.txt");
}
}
fscanf(file, "%*s %d", &num);
or %05d if you have leading zeros and fixed width of 5....
sometimes the fastest way to do things in C++ is to use C. :)
You can create a ctype facet that classifies letters as white space. Create a locale that uses this facet, then imbue the stream with that locale. Having that, you can extract numbers from the stream, but all letters will be treated as white space (i.e. when you extract numbers, the letters will be ignored just like a space or a tab would be):
Such a locale can look like this:
#include <iostream>
#include <locale>
#include <vector>
#include <algorithm>
struct digits_only: std::ctype<char>
{
digits_only(): std::ctype<char>(get_table()) {}
static std::ctype_base::mask const* get_table()
{
static std::vector<std::ctype_base::mask>
rc(std::ctype<char>::table_size,std::ctype_base::space);
if (rc['0'] == std::ctype_base::space)
std::fill_n(&rc['0'], 9, std::ctype_base::mask());
return &rc[0];
}
};
Sample code to use it could look like this:
int main() {
std::cin.imbue(std::locale(std::locale(), new digits_only()));
std::copy(std::istream_iterator<int>(std::cin),
std::istream_iterator<int>(),
std::ostream_iterator<int>(std::cout, "\n"));
}
Using your sample data, the output I get from this looks like this:
46000
39850
38000
Note that as it stands, I've written this to accept only digits. If (for example) you were reading floating point numbers, you'd also want to retain '.' (or the locale-specific equivalent) as the decimal point. One way to handle things is to start with a copy of the normal ctype table, and then just set the things you want to ignore as space.