【问题标题】:C program to sort characters in a stringC程序对字符串中的字符进行排序
【发布时间】:2010-10-14 13:10:38
【问题描述】:

我用 C 语言编写了这个程序,它逐行读取文件(每行只有一个单词),对字母进行排序,然后在每一行中显示排序后的单词和原始单词。

#include<stdio.h>

int main()
{
  char line[128];
  int i=0;
  int j;
  int length;

  while(fgets(line,sizeof line,stdin) != NULL)
  {
    char word[128];

    for (i=0; line[i] != '\0'; i++)
    {
      word[i]=line[i];
    }

    while (line[i] != '\0')
      i++;

    length=i;

 for (i=length-1; i >=0; i--)
    {
      for (j=0; j<i; j++)
      {
        if (line[j] > line[i])
        {
          char temp;
          temp = line[j];
          line[j] = line[i];
          line[i]=temp;
        }
      }
    }
    printf("%s %s",line,word);

  }
  return 0;
}

我正在使用以下 bash 命令编译和运行它。

gcc -o sign sign.c
./sign < sample_file | sort > output

原始文件(sample_file)如下所示:

computer
test
file
stack
overflow

输出文件是这样的:

ackst stack
cemoprtu computer
efil file
efloorvw overflow
er
estt test
ter
ter

我有两个问题:

  1. 输出文件的开头有一堆换行符(即在实际文本开始之前大约有 5-7 个空白行)
  2. 为什么最后会打印两次“ter”?

PS - 我知道这些都是非常基本的问题,但我才刚刚开始使用 C / bash 上课,我不确定我哪里出错了。

【问题讨论】:

  • 你可以使用string.h函数吗?因为使用strlenstrcpy 会更简单。目前,您不会在复制后以 NUL 终止 word
  • 我想我可以使用它
  • 这是作业吗?您可能想要标记它。如果是这样,很遗憾您仅限于 C,它会是其他几种语言的单行代码。
  • 是的。如果是 C# 或 C++,那就轻而易举了。
  • 为什么这个标签是bash

标签: c


【解决方案1】:

问题 1

在这段代码之后,变量line包含一行文本,包括字符串末尾的换行符

while(fgets(line,sizeof line,stdin) != NULL)
{

这就是您获得“额外”换行符的原因。换行符的 ASCII 值小于“A”的 ASCII 值。这就是为什么换行符在每个字符串的开头结束,一旦你对字符进行了排序。例如。 "computer\n" 变成 "\ncemoprtu"。

要解决这个问题,您可以在 for 循环之后去除字符串末尾的换行符

if(i > 0 && word[i-1] == '\n')
{
  word[i-1] = '\0';
  line[i-1] = '\0';
  --i;
}

...

printf("%s %s\n",line,word); /* notice the addition of the newline at the end */

这恰好也解决了问题 2,但请继续阅读,看看出了什么问题。

问题 2

循环之后

for (i=0; line[i] != '\0'; i++) { /* */ }

字符串word 不会以空值结尾(除非是靠运气,因为它是准备好的随机未初始化内存)。这就是你得到“ter”的原因,因为这是你将“计算机”这个词复制到word时留下的数据的一部分。

问题 3

循环之后

for (i=0; line[i] != '\0'; i++) { /* */ }

line[i] != '\0' 的值将始终为 false。这意味着这段代码什么也不做

while (line[i] != '\0')
  i++;

如果我使用 goto 将 for 循环和 while 循环替换为基本相同的代码,问题可能会更加明显:

i=0;
begin_for_loop:
if(line[i] != '\0')
{
  {
    word[i]=line[i];
  }
  i++;
  goto begin_for_loop;
}

begin_while_loop:
if(line[i] != '\0')
{
  i++;
  goto begin_while_loop;
}

(顺便说一句,如果你提到使用 goto,大多数专业程序员都会对你大笑到大喊大叫 :) 我只是在这里用它来说明这一点)

我觉得很方便的一个技巧是在一张纸上画出我的数组、变量等,然后跟踪我的代码的每一行(再次在纸上)以调试它是如何工作的。

【讨论】:

  • 行(因为它是一个数组)不会总是以空值结尾吗?在那种情况下,为什么 line[i] != '\0' 总是假的?
  • fgets 将空终止它写入的任何缓冲区。因此,line[i] == '\0' 将是真的。换一种方式想一想:for 循环仅在line[i] != '\0' 时退出。然后您立即再次进行相同的检查。当然你会得到同样的答案。
  • 但是要回答你问的 exact 问题:“行(因为它是一个数组)不会总是以空值结尾吗?”。不,数组中可以有任何值。除非您手动执行此操作,或者您调用的函数保证执行此操作,否则它们永远不能保证为空终止。 fgets 保证,但 word[i]=line[i]; 没有。您必须自己终止 word(只需在 for 循环后添加:word[i] = '\0';)。
  • 你说 for 循环仅在 line[i] != '\0' 时退出。不是 for 循环运行的时间长到 line[i] != '\0' 吗?还是我在这里遗漏了一些非常基本的东西?
  • @xbonez:哎呀!是的,我忘了说“评估为假” :) 我应该说“只有当line[i] != '\0' 评估为假时,for 循环才会退出。然后你立即再次进行相同的检查。当然你会得到相同的答案。”
猜你喜欢
  • 2012-02-24
  • 1970-01-01
  • 1970-01-01
  • 2013-11-21
  • 2011-07-28
  • 1970-01-01
  • 2012-08-31
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多