我想合并文本文件中的数据排序。(逐行)
为了使我的合并排序起作用,我必须逐行读取数据,并将它们放入数组中以便对它们进行排序。 (我们只知道每行最多10000个整数) 所以我做了我的研究并尝试了这些方法:
问题:我不知道char数组的最大长度。此外,声明一个巨大的数组可能会导致缓冲区溢出。
来源:How many chars can be in a char array?
问题:相同。我不知道有多少整数排成一行。所以我不会对“%d”部分感到满意。(不知道应该有多少)
fscanf
以char数组的形式输入,并使用strtok/strtol
。问题:相同。由于我不知道长度,我不能做像
这样的事情char *data;
data = malloc(sizeof(char) * datacount);
因为“数据帐户”未知。
有什么出路吗?
示例输入:
-16342 2084 -10049 10117 2786
3335 3512 -10936 5343 -1612 -4845 -14514
示例输出:
-16342 -10049 2084 2786 10117
-14514 -10936 -4845 -1612 3335 3512 5343
答案 0 :(得分:1)
你说:
我们只知道每行最多有10000个整数
所以就去吧。符合标准的编译器(和环境)应该能够定义最多65,535个对象的数组。 10,000远不如此,只需定义一个静态数组:
int a[10001], n;
int main() {
// Open file
n = 0;
while (fscanf(fp, " %d", &a[n]) == 1) {
// Process a[n]
n++;
}
}
如果您了解平台规范(如sizeof(int) == 4
),则可以假设整数长度。例如,32位整数的最大长度为11个字符(在-2147483648处)左右。然后,您可以定义具有计算长度的char数组。
答案 1 :(得分:0)
您确实可以使用fscanf
来读取各个整数。除此之外,您还需要了解指针和malloc
,还要了解realloc
。
您只需执行类似
的操作int temporary_int;
int *array = NULL;
size_t array_size = 0;
while (fscanf(your_file, "%d", &temporary_int) == 1)
{
int *temporary_array = realloc(array, (array_size + 1) * sizeof(int));
if (temporary_array != NULL)
{
array = temporary_array;
array[array_size++] = temporary_int;
}
}
在该循环之后,如果array
不是空指针,那么它将包含文件中的所有整数,无论有多少整数。大小(元素数量)位于array_size
变量中。
看到更新后,您可以更轻松地了解所需内容。
在伪代码中很容易:
while(getline(line))
{
array_of_ints = create_array_of_ints();
for_each(token in line)
{
number = convert_to_integer(token);
add_number_to_array(array_of_ints, number);
}
sort_array(array_of_ints);
display_array(array_of_ints);
}
实际上实现这一点要困难得多,并且在某种程度上取决于您的环境(例如,如果您有权访问the POSIX getline
function)。
如果你有e..g getline
(或类似函数),那么伪代码中的外部循环很容易,并且会看起来它已经做了什么。否则,您基本上必须逐个字符地读取到动态扩展的缓冲区(使用realloc
)以适合整行。
这将我们带到外部循环的内容:将输入拆分为一组值。这个答案中第一个代码片段已经有的基本解决方案,我在循环中根据需要重新分配数组。要分割值,那么strtok
可能是最简单的一个。如果您不关心验证输入,则可以使用strtol
atoi
(如果您需要验证)转换为整数。
请注意,您并非真正需要动态分配数组。在int
仅为"且仅为#34;的当前系统上,将有10000 sizeof(int) == 4
个值40000字节。这个小到足以适应大多数非嵌入式系统的堆栈。
答案 2 :(得分:0)
您没有考虑的一种方法可能是最便携的方法:编写您自己的“从FILE流中读取一个令牌,并将其转换为long;但永远不要越过换行边界”功能:
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
/* Reads one decimal integer (long) from 'input', saving it to 'to'.
Returns: 0 if success,
EOF if end of input,
'\n' if newline (end of line),
'!' if the number is too long, and
'?' if the input is not a number.
*/
int read_long(FILE *input, long *to)
{
char token[128], *end;
size_t n = 0;
long value;
int c;
/* Consume leading whitespace, excluding newlines. */
do {
c = fgetc(input);
} while (c == '\t' || c == '\v' || c == '\f' || c == ' ');
/* End of input? */
if (c == EOF)
return EOF;
/* Newline? */
if (c == '\n' || c == '\r') {
/* Do not consume the newline character! */
ungetc(c, input);
return '\n';
}
/* Accept a single '+' or '-'. */
if (c == '+' || c == '-') {
token[n++] = c;
c = fgetc(input);
}
/* Accept a zero, followed by 'x' or 'X'. */
if (c == '0') {
token[n++] = c;
c = fgetc(input);
if (c == 'x' || c == 'X') {
token[n++] = c;
c = fgetc(input);
}
}
/* Accept digits. */
while (c >= '0' && c <= '9') {
if (n < sizeof token - 1)
token[n] = c;
n++;
c = fgetc(input);
}
/* Do not consume the separator. */
if (c != EOF)
ungetc(c, input);
/* No token? */
if (!n)
return '?';
/* Too long? */
if (n >= sizeof token)
return '!';
/* Terminate token, making it a string. */
token[n] = '\0';
/* Parse token. */
end = token;
errno = 0;
value = strtol(token, &end, 0);
if (end != token + n || errno != 0)
return '?';
/* Save value. */
if (to)
*to = value;
return 0;
}
要前进到下一行,您可以使用例如
/* Skips the rest of the current line,
to the beginning of the next line.
Returns: 0 if success (next line exists, although might be empty)
EOF if end of input.
*/
int next_line(FILE *input)
{
int c;
/* Skip the rest of the current line, if any. */
do {
c = fgetc(input);
} while (c != EOF && c != '\n' && c != '\r');
/* End of input? */
if (c == EOF)
return EOF;
/* Universal newline support. */
if (c == '\n') {
c = fgetc(input);
if (c == EOF)
return EOF;
else
if (c == '\r') {
c = fgetc(input);
if (c == EOF)
return EOF;
}
} else
if (c == '\r') {
c = fgetc(input);
if (c == EOF)
return EOF;
else
if (c == '\n') {
c = fgetc(input);
if (c == EOF)
return EOF;
}
}
ungetc(c, input);
return 0;
}
要读取每一行的长片,您可以使用动态调整大小的缓冲区,跨行共享:
int main(void)
{
long *field_val = NULL;
size_t field_num = 0;
size_t field_max = 0;
int result;
do {
/* Process the fields in one line. */
field_num = 0;
do {
/* Make sure the array has enough room. */
if (field_num >= field_max) {
void *temp;
/* Growth policy; this one is linear (not optimal). */
field_max = field_num + 5000;
temp = realloc(field_val, field_max * sizeof field_val[0]);
if (!temp) {
fprintf(stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
field_val = temp;
}
result = read_long(stdin, field_val + field_num);
if (result == 0)
field_num++;
} while (result == 0);
if (result == '!' || result == '?') {
fprintf(stderr, "Invalid input!\n");
return EXIT_FAILURE;
}
/*
* You now have 'field_num' longs in 'field_val' array.
*/
/* Proceed to the next line. */
} while (!next_line(stdin));
free(field_val);
field_val = NULL;
field_max = 0;
return EXIT_SUCCESS;
}
虽然逐个字符读取输入并不是最有效的方式(它往往比逐行读取略慢),但它的多功能性可以补偿它。
例如,上述代码适用于任何换行符约定(CRLF或\r\n
,LFCR或\n\r
,CR \r
和LF \n
)(但在Windows中)您需要为"b"
指定fopen()
标记,以阻止它自行创建换行符。
逐字段读取方法也很容易扩展到例如CSV格式,包括其特有的引用规则,甚至嵌入的换行符。