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;
}
}
}
}
-
\$\begingroup\$ How is a hyphen the same as a space? \$\endgroup\$Reinderien– Reinderien2023年01月18日 02:03:19 +00:00Commented 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\$Eric– Eric2023年01月18日 02:18:56 +00:00Commented Jan 18, 2023 at 2:18
1 Answer 1
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;
-
\$\begingroup\$ No review on
#define SPACE '-'
as OP already id'ed that typo. \$\endgroup\$chux– chux2023年01月18日 03:51:05 +00:00Commented Jan 18, 2023 at 3:51 -
\$\begingroup\$ iamericfletcher, Post lacks tests to show that code works. Next time, consider a test harness. \$\endgroup\$chux– chux2023年01月18日 04:10:37 +00:00Commented Jan 18, 2023 at 4:10
-
\$\begingroup\$ I'm confused - in
getLine
inmain()
, what would be the size I'm passing into it? Would I declaresize_t size;
and then use that like sowhile ((len = getLine(size, line)) > 0)
? \$\endgroup\$Eric– Eric2023年01月18日 11:37:49 +00:00Commented Jan 18, 2023 at 11:37 -
\$\begingroup\$ Also, would I keep
char line[BUFSIZ];
the way it is inmain()
? \$\endgroup\$Eric– Eric2023年01月18日 11:40:39 +00:00Commented Jan 18, 2023 at 11:40 -
\$\begingroup\$ @iamericfletcher More like in
main()
,char line[BUFSIZ];
...while ((len = getLine(sizeof line, line)) > 0)
. \$\endgroup\$chux– chux2023年01月18日 14:33:55 +00:00Commented Jan 18, 2023 at 14:33