我正在进行套接字编程,因为我在文本文件中有以下详细信息。我想使用制表符空间拆分该文本文件,我需要将它们分配给变量我该如何实现?
这是我的文字文件
001 Coffee maker 10 3000.00
002 Pressure cooker 4 7000.00
003 Blender 10 2500.00
004 Pillow 10 300.00
005 Camera 5 25000.00
006 Washer 5 25000.00
007 Headphone 3 5000.00
008 Mattresses 5 6000.00
009 Heater 3 1000.00
010 Cookware 2 10000.00
答案 0 :(得分:2)
以下是从文件中逐行读取的示例代码,以及制表符处的拆分字符串。
strtok修改传递的源字符串,并在解析时使用静态缓冲区,因此它不是线程安全的。如果行中没有分隔符,则第一次调用strtok将返回整个字符串。你需要处理这种情况。
void split_string(char *line) {
const char delimiter[] = "\t";
char *tmp;
tmp = strtok(line, delimiter);
if (tmp == NULL)
return;
printf("%s\n", tmp);
for (;;) {
tmp = strtok(NULL, delimiter);
if (tmp == NULL)
break;
printf("%s\n", tmp);
}
}
int main(void)
{
char *line = NULL;
size_t size;
FILE *fp = fopen("split_text.txt", "r");
if (fp == NULL) {
return -1;
}
while (getline(&line, &size, fp) != -1) {
split_string(line);
}
free(line);
return 0;
}
答案 1 :(得分:1)
您的问题是使用格式化输入函数scanf
读取格式化输入的罕见情况之一(或读取一行并使用sscanf
})实际上是有道理的。如果您的记录为tab
个分隔值,则您可以制作scanf
格式字符串,以合理干净的方式阅读每个字段。
使用scanf
的关键是始终验证返回(根据您的格式说明符的数量发生的成功转化次数) 格式字符串)。如果要将字符串读入固定缓冲区以防止使用相应的字段宽度修饰符来写入数组或分配边界,则还必须保护数组宽度。
将这些部分放在一起,您可以执行以下操作:
int rtn = scanf (" %7s\t%63[^\t]\t%u\t%lf", /* save scanf return */
tmp.idx, tmp.desc, &tmp.n, &tmp.price);
(你的存储空间大小适当 - 我猜你的字段是什么)
说到存储空间,只要您拥有需要协调的不同types
的数据,就应该考虑struct
。这里,例如,具有合理大小的固定缓冲区的struct
很好,例如
/* constants - index, description width, max records */
enum { IDX = 8, DESC = 64, MAXR = 128 };
typedef struct { /* struct to hold each record values */
char idx[IDX], /* 1st field */
desc[DESC]; /* 2nd field */
double price; /* 4th & 3rd - ordered to put smallest last */
unsigned n;
} rec_t;
为了满足您的存储需求,您只需声明一个rec_t
数组,例如
rec_t record[MAXR] = {{ .idx = "" }}, /* array of struct */
tmp = { .idx = "" }; /* tmp struct for read */
当读入struct数组时,使用临时结构来填充scanf
(或者你正在使用的任何值)的值,然后在验证scanf
返回后(通常很有用)转换),你可以简单地将tmp
结构分配给数组中的下一个元素并增加数组索引,例如
if (rtn == 4) /* validate 4 conversions */
record[n++] = tmp; /* assign tmp to record[n], increment */
我发现在使用scanf
进行输入时,可以更轻松地循环,然后验证返回,检查EOF
,然后在遇到EOF
时简单地断开读取循环(或者你否则满足你的输入需求)。
将所有部分组合在一起,您可以执行以下操作,快乐地跳过上面显示的文件中的空行,仅在scanf
返回表示所有成功转换发生时存储值。该程序读取stdin
上的数据,但您可以轻松修改代码以打开给定的文件名进行读取。
#include <stdio.h>
#include <stdlib.h>
/* constants - index, description width, max records */
enum { IDX = 8, DESC = 64, MAXR = 128 };
typedef struct { /* struct to hold each record values */
char idx[IDX], /* 1st field */
desc[DESC]; /* 2nd field */
double price; /* 4th & 3rd - ordered to put smallest last */
unsigned n;
} rec_t;
int main (void) {
rec_t record[MAXR] = {{ .idx = "" }}, /* array of struct */
tmp = { .idx = "" }; /* tmp struct for read */
unsigned n = 0; /* n - records */
while (n < MAXR) { /* loop while n < max records (MAXR) */
int rtn = scanf (" %7s\t%63[^\t]\t%u\t%lf", /* save scanf return */
tmp.idx, tmp.desc, &tmp.n, &tmp.price);
if (rtn == EOF) /* return EOF? */
break;
if (rtn == 4) /* validate 4 conversions */
record[n++] = tmp; /* assign tmp to record[n], increment */
}
for (unsigned i = 0; i < n; i++) /* output array */
printf ("record[%3u]: %-8s %-24s %3u %9.2f\n", i, record[i].idx,
record[i].desc, record[i].n, record[i].price);
}
示例使用/输出
$ ./bin/readtsv <dat/file.tsv
record[ 0]: 001 Coffee maker 10 3000.00
record[ 1]: 002 Pressure cooker 4 7000.00
record[ 2]: 003 Blender 10 2500.00
record[ 3]: 004 Pillow 10 300.00
record[ 4]: 005 Camera 5 25000.00
record[ 5]: 006 Washer 5 25000.00
record[ 6]: 007 Headphone 3 5000.00
record[ 7]: 008 Mattresses 5 6000.00
record[ 8]: 009 Heater 3 1000.00
record[ 9]: 010 Cookware 2 10000.00
仔细看看,如果您有任何问题,请告诉我。
答案 2 :(得分:0)
您可以使用sscanf()
或strtok()
实现简单的解析器,但请注意,这些解析器都不能正确处理空字段。如果在下一个标签页之前没有字符,则scanf
的{{1}}转换说明符将失败,%[^\t]
会将任何标签序列视为单个分隔符。
这是一个带有特殊效用函数的解决方案,其行为类似于strtok()
,但没有缺点:
strtok()