4
\$\begingroup\$

Problem: to eliminate initial zeroes from numbers besides those followed by '.' or alone. For example if we input a-000123+bc+0000.0008-0000+0001.07 , we will get a-123+bc+0.0008-0+1.07).

Using putchar and getchar only. No strings and pointers involved. I have working code, but I think it's confusing and overloaded. Can someone suggest how it might be simplified or an easier algorithm?

char curr_char = 0, prev_char = 0,flag=0;
while ((curr_char = getchar()) != '&')
{

    if (isdigit(curr_char) && !(isdigit(prev_char))) 
        flag = 1;
    if (flag)
    {
        while (true)
        {
            if (curr_char=='0'); else break; 
            curr_char = getchar(); 
        }

        if (curr_char == '.')putchar('0'); 

        while (true) 
        {
            if (isdigit(curr_char))putchar(curr_char); else break;
            curr_char = getchar(); 
        }

        if (curr_char == '.') 
        {
            putchar('.'); 
            while (isdigit(curr_char = getchar()))putchar(curr_char);
        }
        flag = 0;
    }
    else putchar(curr_char);
    prev_char = curr_char;
}
\$\endgroup\$
1
  • 3
    \$\begingroup\$ You appear to be the same person as @Winplexy. We have already merged several of your accounts, and yet you keep creating more new accounts. What is going on? \$\endgroup\$ – 200_success Dec 14 '14 at 10:59
7
\$\begingroup\$

I see a number of things that could be improved here.

Include the appropriate headers

The code as posted is just a fragment, but only a few additional lines would make it into a complete program. It's usually best to provide a complete program for review. With that said, the missing headers are these:

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdbool.h>

Check for EOF

The code currently looks for the & character, but that's not in the specification. It would probably make more sense to look for the end of file which is EOF.

Restructure your code

Any time you find yourself writing while (true) and then putting a conditional break within the loop, this should be a strong indication that something is not quite right. Also, lines like this:

if (curr_char=='0'); else break; 

are really quite awful. That could easily have been written instead as:

if (curr_char != '0')
    break;

which makes it much easier to understand. Even better would have been to write the loop as:

while (cur_char != '0') 
    curr_char = getchar();

Eliminate unneeded variables

The code defines curr_char, prev_char and flag but it's possible to rework the algorithm to eliminate at least one of these. Also, curr_char and prev_char are good names, but flag is not. It's entirely too generic and doesn't say what it's intended to flag.

Fix your algorithm

If we feed a03+0.0g to this code, it should print a3+0.0g but what it actually prints is a30.0 which is clearly not correct. Part of the problem here is that the implementation of the algorithm is quite convoluted and difficult to follow. For that reason, I would recommend a different approach.

Start with a state machine

Starting with a state machine is a very good way to work out what a program should do in advance of writing any code. Having a design first and then writing code is essential to good programming practice. For example, this state machine can be easily implemented in code. I used an enum to represent the state and then a switch statement to drive the code. In it, each transition is labelled as condition/action.

zero suppression state machine

As an example of how to code this, here's the first part of the code including the code for the nonNum state:

int main() 
{
    enum { nonNum, inInt, inDec, zeroSupp } state = nonNum;
    int ch;
    while ((ch = getchar()) != EOF) {
        switch (state) {
            case nonNum:
                if (ch == '0') {
                    state = zeroSupp;
                } else if (ch > '0' && ch <= '9') {
                    state = inInt;
                    putchar(ch);
                } else {
                    putchar(ch);
                }
                break;
      /* the rest of the state machine goes here */
\$\endgroup\$
5
\$\begingroup\$
char curr_char = 0, prev_char = 0,flag=0;

The getchar function returns an int not a char.

int curr_char = 0, prev_char = 0;

You don't need `flag``.

if (isdigit(curr_char) && !(isdigit(prev_char))) 
    flag = 1;
if (flag)
{

You can just say

if ( isdigit(curr_char) && ! isdigit(prev_char) ) 
{

and drop all references to flag (including the assignment at the end of the loop).

    while (true)
    {
        if (curr_char=='0'); else break; 
        curr_char = getchar(); 
    }

This can be more clearly written:

    while (true)
    {
        if ( curr_char != '0' ) break; 
        curr_char = getchar(); 
    }

The blank then clause was unnecessary.

Even clearer:

    while ( '0' == curr_char )
    {
        curr_char = getchar(); 
    }

There's no need to loop forever and then break. Just gate the loop properly.

    if (curr_char == '.')putchar('0'); 

There's no need to wait, just output the decimal point as well:

    if (curr_char == '.') {
        putchar('0');
        putchar('.');
    }

This will save time later.

    while (true) 
    {
        if (isdigit(curr_char))putchar(curr_char); else break;
        curr_char = getchar(); 
    }

Almost the same except with a statement in the then clause.

    while ( isdigit(curr_char) ) 
    {
        putchar(curr_char);
        curr_char = getchar(); 
    }

Previously it only ran those two statements if the expression was true, and it still does.

    if (curr_char == '.') 
    {
        putchar('.'); 
        while (isdigit(curr_char = getchar()))putchar(curr_char);
    }
    flag = 0;

This code is unnecessary. We already output the decimal point and then any subsequent digits. Also, we eliminated the flag variable.

else putchar(curr_char);

It's generally easier to follow if either both the if and the else clauses use {} or neither do. So change this to

else
{
    putchar(curr_char);
}

It seems like this code has a bug where it doesn't output the first character after a number. And it may not check for the terminating '&' immediately after a number. I haven't tried to fix this.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.