如何修复这个条件的问题?

huangapple go评论61阅读模式
英文:

How do I fix the problem with this condition?

问题

基本上,通过K&R书籍学习后,我遇到了一个关于entab的练习,其目标是在可以替换的情况下正确地将空格更改为\t,以最小化字符的使用。例如:"hello " 可以写成 "hello\t",考虑到8个空格等于一个制表符。我的程序会将连续的8个空格更改为一个\t,也可以在原地保留普通的空格,然而,当我遇到这样的情况:"hihi hihihi " 可以写成 "hihi\thihihi\t",但最后一个制表符未被添加。这是我的代码:

#include <stdio.h>
#define MAXLINE 1000
#define STDTAB  8
#define STDVAL  0

// 函数原型
void removechar(char array[], int character, int number);
int my_getline(char array[MAXLINE]);
void entab(char array[]);

int main() {
  char array[MAXLINE];
  int len = STDVAL;
  while ((len = my_getline(array)) != 0) {
    entab(array);
    printf("%s", array);
  }
  
}

void removechar(char array[], int character, int number) {
  int length;
  int out_idx, in_idx;

  
  // 确定数组的长度
  for (length = STDVAL; array[length] != '\0'; ++length) {
    ;
  }
  // 长度增加一以包括空字符
  ++length;


  // 遍历所有数组元素
  for (out_idx = STDVAL; out_idx <= length; ++out_idx) {
    if (array[out_idx] == character && number > 0) {
      for (in_idx = out_idx; in_idx <= length; ++in_idx) {
        array[in_idx] = array[in_idx+1];
      }
      // 移除一个字符后重新调整长度
      --length;

      // 在继续之前返回外部循环一个索引以检查之前的字符
      --out_idx;

      --number;
    }
  }
}

int my_getline(char array[MAXLINE]) {
  int input, i;
  for (i = STDVAL; (input = getchar()) != EOF && input != '\n' && i < MAXLINE-2; ++i) {
    array[i] = input;
  }
  if (input == '\n') {
    array[i] = '\n'
    ++i;
  }
  array[i] = '\0'
  return i;
}

void entab(char array[]) {
  int i;
  int length = STDVAL;
  int blankcounter = STDVAL;
  int normalchar = STDVAL;
  char copy[MAXLINE];

  for (i = STDVAL; array[i] != '\0'; ++i) {
    ++length;
    if (length > 8) {
      blankcounter = normalchar = STDVAL;
    }

    
    if (array[i] == ' ') {
      ++blankcounter;
    }
    if (array[i] != ' ') {
      ++normalchar;
    }

    // 8个连续的空格将被一个制表符替换
    if (blankcounter == STDTAB && normalchar == STDVAL) {
      array[i-7] = '\t';
      removechar(array, ' ', blankcounter-1);
      blankcounter = STDVAL;
      normalchar = STDVAL;
    } 
    
    if (normalchar <= 7 && normalchar >= 1 && blankcounter >= 2) {
      array[(i+1)-blankcounter] = '\t';
      removechar(array, ' ', blankcounter+1);
      blankcounter = normalchar = STDVAL;
    }
    
  }
}

我已经尝试多次更改条件,更改removechar()函数以及许多其他方法,但要么引起更多问题,要么根本解决不了。在这里,将空格更改为制表符的两种情况:

  1. " "(8个连续的空格)
  2. "dawd "(字符和连续的空格,不超过制表符制表位的限制)

在其他情况下保留空格不变。

英文:

Basically, learning through the K&R book I got into an entab exercice, and its objective is to change blanks by \t correctly when they can be replaced to minimize the use of characters. For example: "hello " can be written as "hello\t" considering a 8 blanks equivalent tab stop. My program is changing 8 blanks sequentially by a \t, and can also leave normal blanks on their places, however, when I have a situation like this: "hihi hihihi " that could be written as "hihi\thihihi\t", the final tab don't gets added. That's my code:

#include <stdio.h>
#define MAXLINE 1000
#define STDTAB  8
#define STDVAL  0
// Functions prototypes
void removechar(char array[], int character, int number);
int my_getline(char array[MAXLINE]);
void entab(char array[]);
int main() {
char array[MAXLINE];
int len = STDVAL;
while ((len = my_getline(array)) != 0) {
entab(array);
printf("%s", array);
}
}
void removechar(char array[], int character, int number) {
int length;
int out_idx, in_idx;
// Determining array length
for (length = STDVAL; array[length] != '\0'; ++length) {
;
}
// Length is increased by one to include the null character
++length;
// Iterate over all array elements
for (out_idx = STDVAL; out_idx <= length; ++out_idx) {
if (array[out_idx] == character && number > 0) {
for (in_idx = out_idx; in_idx <= length; ++in_idx) {
array[in_idx] = array[in_idx+1];
}
// Resize length after removing one character
--length;
// Return one index on outer loop to check the previous character before proceeding
--out_idx;
--number;
}
}
}
int my_getline(char array[MAXLINE]) {
int input, i;
for (i = STDVAL; (input = getchar()) != EOF && input != '\n' && i < MAXLINE-2; ++i) {
array[i] = input;
}
if (input == '\n') {
array[i] = '\n';
++i;
}
array[i] = '\0';
return i;
}
void entab(char array[]) {
int i;
int length = STDVAL;
int blankcounter = STDVAL;
int normalchar = STDVAL;
char copy[MAXLINE];
for (i = STDVAL; array[i] != '\0'; ++i) {
++length;
if (length > 8) {
blankcounter = normalchar = STDVAL;
}
if (array[i] == ' ') {
++blankcounter;
}
if (array[i] != ' ') {
++normalchar;
}
// 8 Blanks sequentially, gets replaced by one \t
if (blankcounter == STDTAB && normalchar == STDVAL) {
array[i-7] = '\t';
removechar(array, ' ', blankcounter-1);
blankcounter = STDVAL;
normalchar = STDVAL;
} 
if (normalchar <= 7 && normalchar >= 1 && blankcounter >= 2) {
array[(i+1)-blankcounter] = '\t';
removechar(array, ' ', blankcounter+1);
blankcounter = normalchar = STDVAL;
}
}
}

I already tried changing the conditions multiple times, changing the removechar() function, and a lot of other things, however, or it causes more problems, or just don't solve it. The idea here to change blanks for tabs in 2 situations:

  1. " " (8 sequential blanks)
  2. "dawd " (Characters and sequential blanks without exceeding the tab stop limit)

And leave the blanks as they were in another situations.

答案1

得分: 1

以下是你要的代码部分的中文翻译:

"tabstop"的概念是将字符串视为具有特定长度的"片段"。将每个片段中的尾随空格替换为单个TAB字符的关键在于扫描原始字符串时测量每个片段的长度。

初学者常常会写太多的代码,使用太多的变量来解决问题。没有必要使用多个函数或过多的变量,它们的值会增加和减少。这些都太难跟踪了!

试试这个:

#include <stdio.h>

#define MAXLINE 1000
#define STDTAB  8

void toTab( char *str ) { // 可变缓冲区可能会被"压缩"。
	size_t s = 0, d = 0, i; // 's'表示源,'d'表示目的地

	while( str
展开收缩
) { // 直到字符串的末尾...
for( i = 0; i < STDTAB && (str[d++] = str
展开收缩
) != '\0'; s++, i++ )
; // 复制最多 'tablength' 个字符。 if( i < STDTAB ) // 保留部分尾随片段不变。 break; // 逆向扫描并用TAB替换这个片段中尾随复制的空格 if( str[ d - 1 ] == ' ' ) { while( str[ d - 1 ] == ' ' ) d--; str[ d++ ] = '\t'; } } str[ d ] = '\0'; // 终止字符串(无论是否压缩)。 } int main( void ) { char test[][MAXLINE] = { "ABCD EFGH", " ABCD EFGH", }; for( int i = 0; i < sizeof test/sizeof test[0]; i++ ) { puts( test[i] ); toTab( test[i] ); puts( test[i] ); } return 0; }

这不涉及尾随的换行符。我认为应该移除所有尾随的空白(减少文本的体积)。

下一个挑战是将带有嵌入式制表符的字符串"展开"为只包含空格的字符串...

编辑
承认我的错误...
上面的代码将在字符串以足够多的空格开头时超出字符串的开头。下面是修正的"逆向扫描"。教训是,总是测试极端情况。

void toTab( char *str ) {
	size_t i = 0, s = 0, d = 0;

	while( str
展开收缩
) {
i = 0; while( i < STDTAB && (str[d] = str
展开收缩
) != '\0' )
s++, i++, d++; // 复制最多 '8' 个字符。 if( i < STDTAB ) break; if( str[ d - 1] == ' ' ) { while( i-- && str[ d - 1] == ' ' ) d--; str[d++] = '@'; } } str[ d ] = '\0'; }
英文:

The concept of a "tabstop" is to think of a string in terms of "fragments" of a certain length. The key to replacing trailing spaces in each fragment with a single TAB character comes from measuring out each fragment as one scans across the original string.

It's common for beginner's to write too much code employing too many variables as they struggle to solve a problem. There's no need for multiple functions or an overabundance of variables that increase and decrease in value. All too much to keep track of!

Try this:

#include &lt;stdio.h&gt;
#define MAXLINE 1000
#define STDTAB  8
void toTab( char *str ) { // mutable buffer may be &quot;condensed&quot;.
size_t s = 0, d = 0, i; // &#39;s&#39;ource and &#39;d&#39;estination
while( str
展开收缩
) { // to the end of the string... for( i = 0; i &lt; STDTAB &amp;&amp; (str[d++] = str
展开收缩
) != &#39;\0&#39;; s++, i++ ) ; // copy up to &#39;tablength&#39; chars. if( i &lt; STDTAB ) // leave partial trailing fragment alone. break; // scan backwards and replace trailing copied SP&#39;s in this fragment with TAB if( str[ d - 1 ] == &#39; &#39; ) { while( str[ d - 1 ] == &#39; &#39; ) d--; str[ d++ ] = &#39;\t&#39;; } } str[ d ] = &#39;\0&#39;; // terminate the string (condensed or not). } int main( void ) { char test[][MAXLINE] = { &quot;ABCD EFGH&quot;, &quot; ABCD EFGH&quot;, }; for( int i = 0; i &lt; sizeof test/sizeof test[0]; i++ ) { puts( test[i] ); toTab( test[i] ); puts( test[i] ); } return 0; }

This does not address a trailing newline. My view is that any/all trailing whitespace should be removed (reducing the volume of the text).

The next challenge is to take a string with embedded tabs, and "expand" it out to a string with SP's...


EDIT<br>
Admitting my mistake...<br>
The above will stray beyond the beginning of the string if the string begins with enough SP's. Below corrects the "reverse scan". The lesson is, always test edge cases.

void toTab( char *str ) {
size_t i = 0, s = 0, d = 0;
while( str
展开收缩
) { i = 0; while( i &lt; STDTAB &amp;&amp; (str[d] = str
展开收缩
) != &#39;\0&#39; ) s++, i++, d++; // copy up to &#39;8&#39; chars. if( i &lt; STDTAB ) break; if( str[ d - 1] == &#39; &#39; ) { while( i-- &amp;&amp; str[ d - 1] == &#39; &#39; ) d--; str[d++] = &#39;@&#39;; } } str[ d ] = &#39;\0&#39;; }

答案2

得分: 0

以下是代码部分的翻译:

#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#define STDTAB  8

// 将结果写入数组[i],从数组[j]读取
void entab(char array[]) {
	size_t i = 0;
	for(size_t j = 0; array[j];) {
		if(array[j] == &#39; &#39;) {
            // 读取前瞻数组[j+l]
			size_t l = 1;
			for(; array[j+l] &amp;&amp; array[j+l] == &#39; &#39; &amp;&amp; l &lt; STDTAB; l++);
			if(l == STDTAB) {
				array[i++] = &#39;\t&#39;;
				j += l;
				continue;
			}
			strncpy(&amp;array[i], &amp;array[j], l);
			i += l;
			j += l;
			continue;
		}
		array[i++] = array[j++];
	}
	array[i] = &#39;\0&#39;;
}

int main() {
	struct {
		char *input;
		const char *expected;
	} tests[] = {
		{(char []) {&quot;        &quot;}, &quot;\t&quot;},
		{(char []) {&quot;dawd    &quot;}, &quot;dawd    &quot;},
		{(char []) {&quot;hihi    hihihi  &quot;}, &quot;hihi    hihihi  &quot;}
	};
	for(size_t i = 0; i &lt; sizeof tests / sizeof *tests; i++) {
		entab(tests[i].input);
		if(strcmp(tests[i].input, tests[i].expected))
			printf(&quot;tests: %zu\n&quot;
				&quot;\tgot: \&quot;%s\&quot;\n&quot;
				&quot;\texpected: \&quot;%s\&quot;\n&quot;,
				i, tests[i].input, tests[i].expected);
	}
}

考虑重新设计接口,以返回一个新的字符串,而不是直接在原始输入上进行修改。

英文:

Your test cases you mention is inconsistent with the requirement:

"hihi hihihi " that could be written as "hihi\thihihi\t"

but the input has 4 and 2 spaces not 8.

#include &lt;stdio.h&gt;
#include &lt;string.h&gt;
#define STDTAB  8
// write to array[i] and read from array[j]
void entab(char array[]) {
size_t i = 0;
for(size_t j = 0; array[j];) {
if(array[j] == &#39; &#39;) {
// read look-ahead array[j+l]
size_t l = 1;
for(; array[j+l] &amp;&amp; array[j+l] == &#39; &#39; &amp;&amp; l &lt; STDTAB; l++);
if(l == STDTAB) {
array[i++] = &#39;\t&#39;;
j += l;
continue;
}
strncpy(&amp;array[i], &amp;array[j], l);
i += l;
j += l;
continue;
}
array[i++] = array[j++];
}
array[i] = &#39;\0&#39;;
}
int main() {
struct {
char *input;
const char *expected;
} tests[] = {
{(char []) {&quot;        &quot;}, &quot;\t&quot;},
{(char []) {&quot;dawd    &quot;}, &quot;dawd    &quot;},
{(char []) {&quot;hihi    hihihi  &quot;}, &quot;hihi    hihihi  &quot;}
};
for(size_t i = 0; i &lt; sizeof tests / sizeof *tests; i++) {
entab(tests[i].input);
if(strcmp(tests[i].input, tests[i].expected))
printf(&quot;tests: %zu\n&quot;
&quot;\tgot: \&quot;%s\&quot;\n&quot;
&quot;\texpected: \&quot;%s\&quot;\n&quot;,
i, tests[i].input, tests[i].expected);
}
}

Consider reworking the interface to return a new string instead of modifying the input in place.

huangapple
  • 本文由 发表于 2023年2月24日 06:01:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/75550768.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定