正如标题所述,我需要编写一个小程序来读取标准输入中的数据,对其进行排序,并将其发送到标准输出。程序应该使用一个参数来告诉它一个记录的长度(以字节为单位)。下面是我测试它的方法:
printf 'D\x00C\x00\x00B\x00A' | ./binsort 2 | od -c上面的输出应该如下所示:
0000000 \0 A \0 B C \0 D \0
0000010以下是我到目前为止(binsort.c)的情况:
#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..。但事实并非如此。
我正在考虑使用qsort对malloc/realloc-allocated数组进行排序。然而,我从来没有经历过这些,实际上几天来我的头都撞在了这个小小的公用设施上。有人能帮忙吗?
人们问这是不是家庭作业..。我公司的开发人员不想用这个来调试他们的项目的输出。
发布于 2009-02-09 22:08:11
不要使用scanf()和printf()。这些应该与文本数据一起使用。由于您处理的是二进制数据,因此需要使用较低级别的fread()和fwrite()函数。由于您不知道有多少总数据,所以必须使用动态数据结构(例如可调整大小的数组)来存储输入。你不能在线处理这些数据--你必须把它全部读出来,排序,然后把它写出来。您的排序函数也是错误的--您只是比较每个记录的前4个字节,如果记录大小不是4个字节,则这是错误的。使用memcmp()代替。
这应该是可行的:
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;
}发布于 2009-02-09 22:33:57
P.S.人们问这是否是家庭作业.我公司的开发人员不想用这个来调试他们的项目的输出.
如果这不是作业,这是在Linux下,为什么不使用包含的“排序”程序呢?
例如:
% printf 'D\x00C\x00\x00B\x00A' | sort -z | od -c
0000000 \0 A \0 B \0 C \0 D \0
0000011FSF/GNU排序提供了-z选项:
-z, --zero-terminated
end lines with 0 byte, not newline修正如下:
好吧,既然你还想要你自己的代码.
看看其他人是如何发布基于STL的方法的。
注意在比较中使用struct函子(通过stl::sort())。而且,如果您愿意,可以使用ostream_iterator(cout,"\n")来使事物更具可读性。
#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';。c1或c9也是如此。我可以在该数组中包含任意数量的空字符。(当然,这取决于数组的大小。)
现在,当使用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')。
发布于 2009-02-09 21:50:10
fgets和fprintf处理字符串(在C中它们是以特殊字符\0结尾的char数组)。
对二进制数据使用fread和fwrite。qsort很好,只是您的比较函数需要逐字节比较entry_size字节,而不仅仅是从void转换到int,这是不安全的,而且可能是不正确的。
https://stackoverflow.com/questions/530116
复制相似问题