0

I'm doing UVa Problem 10082 and I'm trying to read in some sample input to test my solution. However, when I read in the text '''CCC it outputs ;;XXX. Note that there are only 2 semi-colons, when there should be 3 since there are 3 single quotes in the input. Why is getline() ignoring the first single quote?

Here's my code:

#include <iostream>
#include <string>

using namespace std;

char mapToWertyu(char c)
{
    char newC = c;
    char wertyu[] = {'1','2','3','4','5','6','7','8','9','0','-','=',
                     'Q','W','E','R','T','Y','U','I','O','P','[',']','\\',
                     'A','S','D','F','G','H','J','K','L',';','\'',
                     'Z','X','C','V','B','N','M',',','.','/'};
    char qwerty[] = {'~','1','2','3','4','5','6','7','8','9','0','-','=',
                     'Q','W','E','R','T','Y','U','I','O','P','[',']','\\',
                     'A','S','D','F','G','H','J','K','L',';','\'',
                     'Z','X','C','V','B','N','M',',','.','/'};
    if(c=='A' || c=='Q' || c=='Z')
        return c;

    for(int i = 0; i < 47; ++i)
    {
        if(wertyu[i]==c)
        {
            newC = qwerty[i];
            break;
        }
    }
    return newC;
}

int main()
{
    string input;
    while(cin.peek()!=-1)
    {
        getline(cin,input);
        for(int i = 0; i < input.length(); ++i)
        {
            if(input[i]!='\\')
                cout << mapToWertyu(input[i]);
        }
        cin.ignore(1,'\n');
        cout << endl;
    }
    return 0;
}
muttley91
  • 12,278
  • 33
  • 106
  • 160
  • I cut and pasted your code into VS2013 and ran it. On that system '''CCC produces ;;;XXX – Dweeberly Nov 04 '13 at 19:06
  • 1
    @Dweeberly Just a guess, but the input he's having problems with is probably not the first line. He ignores the first character of every line but the first. – James Kanze Nov 04 '13 at 19:20

1 Answers1

1

Because you tell it to. What do you thing std::cin.ignore( 1, '\n' ) should do, if not ignore a character. std::getline extracts the '\n' character, even if it doesn't put it into the string.

For the rest, you're not doing the input right. For starters, std::cin.peek() will return an integer in the range [0...UCHAR_MAX] or EOF. EOF is often defined to be -1, but this is certainly not guaranteed. But more generally: why not use the usual idiom:

while ( std::getline( std::cin, input ) ) {
    //  ...
}

You also construct the arrays in mapToWertyu each time you call it. That is definitly not what you want to do. You could use just one static array, indexed directly by the character, bug this does make the program dependent on the encoding. To use two arrays, however:

static char const wertyu[] = { ... };
static char const qwerty[] = { ... };
char const* pos = std::find( std::begin( wertyu ), std::end( wertyu ), c );
return pos == std::end( wertyu )
    ? c
    : qwerty[ pos - std::begin( wertyu ) ];

Solves the problem in a much simpler manner. (And there's no need to special case 'A', 'Q' and 'Z'. If you don't want to transcode them, just don't put them in the table.)

Or...

struct Map
{
    char from;
    char to;
};
static Map const map[] =
{
    { '1', '~' },
    { '2', '1' },
    //  ...
};
Map const* pos = std::find_if( std::begin( map ), std::end( map ),
                    []( char ch ) { return c == ch; } );
return pos == std::end( map )
    ? c
    : pos->to;

This has the advantage of making the exact mapping visible.

Or if you're 100% sure you'll never have to worry about threads:

struct Map
{
    char from;
    char to;
};
static Map map[] =
{
    { '1', '~' },
    { '2', '1' },
    //  ...
    {  0,   0  }
};
Map* sentinal = std::end( map ) - 1;
sentinal->to = sentinal->from = c;
Map const* pos = std::find_if( std::begin( map ), std::end( map ),
                    []( Map const& entry ) { return c == entry.from; } );
return pos->to;

By inserting the sentinal, you can be sure that the entry will be found.

Or you could sort the map, and use std::lower_bound.

Also, why do you call the function mapToWertyu, when it maps to querty?

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Addressing the problem at hand, that was a really silly mistake as I'd even said to myself "remember to remove the `cin.ignore()`". For the rest of the answer, not that it's any excuse, but I'd just been throwing this code together quickly for an answer. I'm glad you wrote this anyway, as I'm somewhat unfamiliar with everything C++ has to offer, and you've suggested far better ways to solve this problem. – muttley91 Nov 04 '13 at 20:25
  • Actually just remembered I was originally going to use a Map, but I thought it'd be quicker to just throw the two arrays together. – muttley91 Nov 04 '13 at 20:28
  • @rar I used lambdas (which is C++11) for convenient. In pre-C++11, you'd have to move the code in the lambdas out into a separate functional object. (I do this often enough that I've a template `StaticMap` which defined the functional objects for me.) – James Kanze Nov 05 '13 at 09:19