5
\$\begingroup\$

Continuing my K&R journey!

Please see below for my solution to:

Exercise 1-20: Write a program detab that replaces tabs in the input with the proper number of blanks to space to the next tab stop. Assume a fixed set of tab stops, say every n columns.

#include <stdio.h>
#define TAB '\t' // character to look for
#define SPACE '-' // tab replacement char
#define TABSIZE 8 // fixed set of tab stops
#define MAXLINE 8000 // maximum number of chars in a line
static int getLine(char line[]);
static void processLine(char line[], int lineCharCount);
int main() {
 int len; //length of line
 char line[BUFSIZ]; //string constant array for appending ch
 while ((len = getLine(line)) > 0) {
 processLine(line, len);
 }
 return 0;
}
static int getLine(char line[]) {
 int c;
 int i;
 for (i = 0; i < MAXLINE - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
 line[i] = c;
 }
 if (c == '\n') {
 line[i] = c;
 ++i;
 }
 line[i] = '0円';
 return i;
}
static void processLine(char line[], int lineCharCount) {
 int nonTabCount; // running count of characters up until \t is encountered
 int i; // for loop
 nonTabCount = 0;
 for (i = 0; i < lineCharCount; i++) {
 if (line[i] == TAB) {
 nonTabCount++;
 line[i] = SPACE;
 putchar(line[i]);
 while (TABSIZE - nonTabCount > 0) {
 putchar(SPACE);
 nonTabCount++;
 }
 nonTabCount = 0;
 } else {
 putchar(line[i]);
 nonTabCount++;
 if (nonTabCount >= TABSIZE) {
 nonTabCount = 0;
 }
 }
 }
}
Reinderien
70.9k5 gold badges76 silver badges256 bronze badges
asked Jan 18, 2023 at 1:21
\$\endgroup\$
2
  • \$\begingroup\$ How is a hyphen the same as a space? \$\endgroup\$ Commented Jan 18, 2023 at 2:03
  • 3
    \$\begingroup\$ Oh dear, I forgot to change back to space..... having hyphens was helpful while building the program. Gosh darnit. \$\endgroup\$ Commented Jan 18, 2023 at 2:18

1 Answer 1

4
\$\begingroup\$

Overall: Aside from bug, a very good post.


Nice layout & formatting

Good object names

Bug: Inconsistent size

With char line[BUFSIZ]; and C definition of "The value of the macro BUFSIZ shall be at least 256.", calling getLine(line) is a problem. getLine() assumes the buffer is MAXLINE or 8000.

Better to use the same. Even better, pass the size into getLine().

// static int getLine(char line[]) {
static size_t getLine(char line[], size_t size) {

I like size first to allow static code analysis with:

static size_t getLine(size_t size, char line[size]) {

In this case, best to initialize to handle tiny sizes.

// int c;
int c = 0;

Pedantic: string size

String size can exceed INT_MAX. Better to use size_t to express lineCharCount to handle all strings.

// processLine(char line[], int lineCharCount)
processLine(char line[], size_t lineCharCount)

Then use size_t i; in the function.

Simplify

Simpler and handles unsigned math well - no overflow.

// while (TABSIZE - nonTabCount > 0) {
while (TABSIZE > nonTabCount) {

Printing

I'd print the processed line afterwards and avoid printing in processLine(). Of course that is tricky as the line grows.

Why change line[]?

processLine() has line[i] = SPACE;, but why change only 1 space? If anything the de-tabify process might add multiple spaces and that complicates things. Code instead could putchar(SPACE); and leave line[] unchanged.

If processLine() changed to leave char line[] unchanged, consider making that paramter const char line[].

String?

With processLine(char line[], int lineCharCount), I'd expect the function received a pointier to a string, in which case, the lineCharCount is not needed. Instead iterate until a null character is found.

Yet by passing lineCharCount does allow handling of a line of input with a null character that was read - an advance functionality.

Fixed tab size

"Assume a fixed set of tab stops, say every n columns." --> that sounds like n should be an option at program start to de-tabify at different values. Maybe that is for the next iteration of code.

Documentation

Coding goal deserves to be in the code as a comment.

Advanced: long line

When getLine() fills the buffer, yet the line is still not done, consider how one would re-work the function to convey that incomplete read to the caller and if the remainder of the line should remain unread.

Declare and initialize in one step

// int nonTabCount;
// nonTabCount = 0;
int nonTabCount = 0;
answered Jan 18, 2023 at 3:29
\$\endgroup\$
7
  • \$\begingroup\$ No review on #define SPACE '-' as OP already id'ed that typo. \$\endgroup\$ Commented Jan 18, 2023 at 3:51
  • \$\begingroup\$ iamericfletcher, Post lacks tests to show that code works. Next time, consider a test harness. \$\endgroup\$ Commented Jan 18, 2023 at 4:10
  • \$\begingroup\$ I'm confused - in getLine in main(), what would be the size I'm passing into it? Would I declare size_t size; and then use that like so while ((len = getLine(size, line)) > 0)? \$\endgroup\$ Commented Jan 18, 2023 at 11:37
  • \$\begingroup\$ Also, would I keep char line[BUFSIZ]; the way it is in main()? \$\endgroup\$ Commented Jan 18, 2023 at 11:40
  • \$\begingroup\$ @iamericfletcher More like in main(), char line[BUFSIZ]; ... while ((len = getLine(sizeof line, line)) > 0). \$\endgroup\$ Commented Jan 18, 2023 at 14:33

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.