Reputation: 5673
I just finished the first chapter of The C Programming Language and there are a few exercises before moving on. I already completed the one to replace the tab character with spaces which was fairly easy, but I am stuck on the one to replace space characters with the proper amount of tabs and spaces to achieve the same spacing.
My implementation "sometimes" works, so essentially it doesn't work. Here is the function:
#define TABLEN 5
// entab: replace consecutive spaces of length TABLEN with the tab character
void entab(char string[])
{
int i, consec;
int to, from, tabloc;
consec = 0;
for (i = 0; string[i] != '\0'; ++i) {
// count consecutive spaces in a string
if (string[i] == ' ') ++consec;
else consec = 0;
if (consec >= TABLEN) {
// set location to insert tab character
tabloc = (i - TABLEN) + 1;
for (to = tabloc, from = i;
string[from] != '\0'; ++from, ++to)
{
// replace space characters
string[to] = string[from];
}
string[tabloc] = '\t';
string[to] = '\0';
i = tabloc;
consec = 0;
}
}
}
This function is extremely inconsistent in working successfully to the point where there isn't even a pattern of when it does and doesn't work. By "doesn't work", I mean one of two situations. 1.) the spaces are deleted and no tab character is inserted, or 2.) the spaces are deleted, a tab character is inserted, but somehow an extra space is added in. These issues have led me to realize that the problem exists in the loop that replaces the spaces, but I'm so new to C that I have no idea what is wrong. Can someone point me in the right direction here?
Upvotes: 0
Views: 3427
Reputation: 4357
Here's how tabs work:
If you typed
Tab
v v v v
------------------------
| a
|a a
|aa a
|aaa a
|aaaa a
Notice how if there was 5, 4, 3, 2, or 1 spaces, they all could be equally represented as a tab. This is why 5 spaces doesn't equal a tab (even when the tab size is set to 5). Consider this case as well:
v v v v
------------------------
|aaaa a
| 12345
But when you replace those 5 spaces with a tab, you get:
v v v v
------------------------
|aaaa a
| 12345
Here's an working example:
#include <stdio.h>
#include <string.h>
void Print_As_String(char * buffer, unsigned int size);
void Print_As_Hex(char * buffer, unsigned int size);
void Convert_Tab_To_Space(char * buffer, unsigned int size, unsigned int tab_size);
int main(unsigned int argc, char * argv[]){
unsigned int i = 0;
unsigned int arg_length = 0;
if (argc <= 1){
printf("Usage: \"Text with spaces\", \"More text with spaces\", etc\n");
return -1;
}
for (i = 1; i < argc; i++){
arg_length = strlen(argv[i]);
Print_As_String (argv[i], arg_length);
Print_As_Hex (argv[i], arg_length);
Convert_Tab_To_Space(argv[i], arg_length, 8);
Print_As_String (argv[i], arg_length);
Print_As_Hex (argv[i], arg_length);
}
return 0;
}
void Print_As_String(char * buffer, unsigned int size){
printf("%.*s\n", size, buffer);
}
void Print_As_Hex(char * buffer, unsigned int size){
unsigned int i = 0;
const char hex_table[16] = "0123456789ABCDEF";
for (i = 0; i < size; i++){
unsigned char high_byte = 0;
unsigned char low_byte = 0;
high_byte = (buffer[i] & 0xF0) >> 4;
low_byte = (buffer[i] & 0x0F) >> 0;
putc(hex_table[high_byte], stdout);
putc(hex_table[low_byte], stdout);
putc(' ', stdout);
}
putc('\n', stdout);
}
void Shift_Characters_Left(char * buffer,
unsigned int position_start,
unsigned int position_end,
unsigned int size);
void Convert_Tab_To_Space(char * buffer, unsigned int size, unsigned int tab_size){
unsigned int i = 0;
unsigned int x = 0; /* x is used
for getting the position in
the current line. This is
different from 'i' because
there may be many lines in
one string.
*/
for (i = 0; i < size; i++){
if (buffer[i] == '\t'){ /* the x coordinates
change in this fashion when a new
tab is found.
*/
x += tab_size - (x % tab_size);
} else if (buffer[i] == ' '){
unsigned int tab_remainder = 0; // how many spots are left for a tab
unsigned int space_i = 1; // space index
tab_remainder = (x % tab_size);
while ((i + space_i) < size){
/* if the space count makes up for the
missing spots in the tab remainder,
replaces the spaces with a tab
*/
if ((tab_remainder + space_i) == tab_size){
Shift_Characters_Left(buffer, // move the spot at the end of
i + space_i, // the spaces to the spot at
i + 1, // the start of the spaces
size);
buffer[i] = '\t';
}
if (buffer[i + space_i] != ' '){
i += space_i;
break;
}
space_i++;
}
} else if (buffer[i] == '\n'){
x = 0;
} else {
x++;
}
}
}
void Shift_Characters_Left(char * buffer,
unsigned int position_start,
unsigned int position_end,
unsigned int size){
memmove(buffer + position_end,
buffer + position_start,
size - position_end);
memset(&buffer[position_start], 0, (size - 1) - position_start);
}
But there is an unaddressable error I get when I test this problem. I think it's something i'm doing wrong with memset
(probably an off-by-one error).
Upvotes: 4
Reputation: 2495
The following works fine. Check the differences in the indexes
#include <stdio.h>
#define TABLEN 5
// entab: replace consecutive spaces of length TABLEN with the tab character
void entab(char string[])
{
int i, consec;
int to, from, tabloc;
printf("%s\n",string);
consec = 0;
for (i = 0; string[i] != '\0'; ++i) {
// count consecutive spaces in a string
if (string[i] == ' ') ++consec;
else consec = 0;
if (consec >= TABLEN) {
// set location to insert tab character
tabloc = (i - TABLEN) + 1;
for (to = tabloc+1, from = i+1;
string[from] != '\0'; ++from, ++to)
{
// replace space characters
string[to] = string[from];
}
string[tabloc] = '\t';
string[to] = '\0';
i = tabloc+1;
consec = 0;
}
}
printf("%s",string);
}
int main(void) {
// your code goes here
char a[] = "hello wor l d";
entab(a);
return 0;
}
Upvotes: 2