首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在Linux中使用stdio读取和排序管道数据

在Linux中使用stdio读取和排序管道数据
EN

Stack Overflow用户
提问于 2009-02-09 21:39:57
回答 6查看 1.2K关注 0票数 3

正如标题所述,我需要编写一个小程序来读取标准输入中的数据,对其进行排序,并将其发送到标准输出。程序应该使用一个参数来告诉它一个记录的长度(以字节为单位)。下面是我测试它的方法:

代码语言:javascript
运行
复制
printf 'D\x00C\x00\x00B\x00A' | ./binsort 2 | od -c

上面的输出应该如下所示:

代码语言:javascript
运行
复制
0000000  \0   A  \0   B   C  \0   D  \0
0000010

以下是我到目前为止(binsort.c)的情况:

代码语言:javascript
运行
复制
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

using namespace std;


void print_usage()
{
        printf("%s\n", "Usage: ");
}


int compare (const void * a, const void * b) // the compare function for qsort... might need some work
{
  return ( *(int*)a - *(int*)b );
}


int main(int argc, char *argv[])
{
        if (argc != 2 || stdin == NULL) // check for argument and piped input
        {
                print_usage();
                exit(EXIT_FAILURE);
        }

        int entry_size = atoi(argv[1]);

        if (entry_size <= 0 || entry_size >= INT_MAX) // check for valid range of entry size
        {
                print_usage();
                exit(EXIT_FAILURE);
        }

        char *input = new char[entry_size]; // to hold single record

        while (fgets(input, entry_size, stdin) != NULL)
        {
                printf("%s", input); // output single record we just read
        }

        exit(EXIT_SUCCESS);
}

然后用g++ binsort.c -o binsort编译。

上面的代码编译了,但没有输出printf发送给它的数据。它应该以2字节块输出.比如D\0 C\0 \0B \0A..。但事实并非如此。

我正在考虑使用qsortmalloc/realloc-allocated数组进行排序。然而,我从来没有经历过这些,实际上几天来我的头都撞在了这个小小的公用设施上。有人能帮忙吗?

人们问这是不是家庭作业..。我公司的开发人员不想用这个来调试他们的项目的输出。

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2009-02-09 22:08:11

不要使用scanf()printf()。这些应该与文本数据一起使用。由于您处理的是二进制数据,因此需要使用较低级别的fread()fwrite()函数。由于您不知道有多少总数据,所以必须使用动态数据结构(例如可调整大小的数组)来存储输入。你不能在线处理这些数据--你必须把它全部读出来,排序,然后把它写出来。您的排序函数也是错误的--您只是比较每个记录的前4个字节,如果记录大小不是4个字节,则这是错误的。使用memcmp()代替。

这应该是可行的:

代码语言:javascript
运行
复制
char *input = NULL;
size_t input_size = 0;
int num_items = 0;
int entry_size;

int compare_func(const void *e1, const void *e2)
{
  return memcmp(e1, e2, entry_size);
}

int main(int argc, char **argv)
{
   // ...
  char *datum = malloc(entry_size);  // check for NULL
  input_size = 4096;
  input = malloc(input_size);  // check for NULL

  while(1)
  {
    if(fread(datum, 1, entry_size, stdin) < entry_size)
      break;
    size_t new_size = (num_items + 1) * entry_size;
    if(new_size > input_size)
    {
      input = realloc(input, input_size * 2);  // check for NULL
      input_size *= 2;
    }
    memcpy(input + num_items * entry_size, datum, entry_size);
    num_items++;
  }

  qsort(input, num_items, entry_size, compare_func);

  fwrite(input, entry_size, num_items, stdout);

  return 0;
}
票数 5
EN

Stack Overflow用户

发布于 2009-02-09 22:33:57

P.S.人们问这是否是家庭作业.我公司的开发人员不想用这个来调试他们的项目的输出.

如果这不是作业,这是在Linux下,为什么不使用包含的“排序”程序呢?

例如:

代码语言:javascript
运行
复制
% printf 'D\x00C\x00\x00B\x00A' | sort -z | od -c
0000000  \0   A  \0   B  \0   C  \0   D  \0
0000011

FSF/GNU排序提供了-z选项:

代码语言:javascript
运行
复制
-z, --zero-terminated
        end lines with 0 byte, not newline

修正如下:

好吧,既然你还想要你自己的代码.

看看其他人是如何发布基于STL的方法的。

注意在比较中使用struct函子(通过stl::sort())。而且,如果您愿意,可以使用ostream_iterator(cout,"\n")来使事物更具可读性。

代码语言:javascript
运行
复制
#include <iostream>
#include <fstream>
#include <iomanip>
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>

using namespace std;

   /* ifstream won't set EOF-state until we try to read past the end-of-file.*/
   /* (Makes for lots of grief trying to read in files...)                   */
inline bool
IsStreamStillGood( istream & theStream )
{
  return theStream && (theStream . peek() != EOF);
}

template<class TYPE> inline void DELETE( TYPE * x)
{
  delete x;
  x = (TYPE *) NULL;
}

struct FUNCTOR
{
  bool operator()(const string & x, const string & y) { return x < y; }
};


int
main(int argc, char **argv)
{
  istream *       stream;
  vector<string>  v;

  UASSERT( argc, >, 1 );

  const int recordSize = atoi( argv[1] );
  char      buffer [ recordSize + 1 ];

  UASSERT( recordSize, >, 0 );


  if ( argc > 2 )
    stream = new ifstream( argv[2] );
  else
    stream = & cin;


  while ( IsStreamStillGood( * stream ) )
  {
    stream-> read( buffer, recordSize );
    v.push_back( string( buffer, stream->gcount() ) );
  }

  UASSERT( v.back().size(), ==, size_t(recordSize) );


  FUNCTOR functor;
  sort( v.begin(), v.end(), functor );

  copy( v.begin(), v.end(), ostream_iterator<string>(cout) );


  if ( argc > 2 )
    DELETE(stream);
}

修正(再次),增加:

关于在STL方法中使用字符串的问题:由于输入中可能存在空字符('\0'),所以字符串不会因此而混淆吗?有人建议使用char[],但我怀疑同样的问题会产生

如果我有char c10;,我可以说c = '\0';c1c9也是如此。我可以在该数组中包含任意数量的空字符。(当然,这取决于数组的大小。)

现在,当使用c++作为c-字符串时,就会出现一个问题,即它的长度。它是一个字符长还是9个字符?通常,在C中,这取决于第一个空字符出现的位置。

因此,诸如printf(%s)、s(%s)、strncat()、strncpy()、strncmp()等都对二进制数据数组中嵌入的NULL ('\0')字符并不满意。

但是C++ std::string独立地跟踪长度。至少看起来是这样的,它允许这样的事情:myString.append( 10,'\0‘);

因此,通过使用流-> read (buffer,recordSize),我们读取了一定数量的字符(字节)。我们并不关心它们是否为空('\0')。一切都很好。只需给我recordSize字节数。

通过使用stream->gcount() v.push_back( string (buffer,stream->gcount()))创建字符串,我们将一个包含字节的新字符串向前推回。同样,我们也不在乎它们是否为空('\0')。我们只需要所有的stream->gcount()字节。

排序使用函子,它使用operator<( contained &,contained &),它使用string::compare(),它将再次使用字符串的长度,而不关心字符串中实际包含的数据。Nulls ('\0')只是很好。

现在,如果我们尝试使用v.back().c_str(),那么我们就没有长度,所以空值会使我们感到困惑。但是,只要我们使用包含数据和长度的string对象(例如v.back()),我们就可以了。

这就带来了我们的输出。我们再次输出字符串,而不是myString.c_str(),因此字符串中的所有字符都会被打印出来。包括零('\0')。

票数 3
EN

Stack Overflow用户

发布于 2009-02-09 21:50:10

fgets和fprintf处理字符串(在C中它们是以特殊字符\0结尾的char数组)。

对二进制数据使用fread和fwrite。qsort很好,只是您的比较函数需要逐字节比较entry_size字节,而不仅仅是从void转换到int,这是不安全的,而且可能是不正确的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/530116

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档