在C中将字符拆分成单词

我将以下格式的行存储到char中。 每个单词用制表符分隔。

BSSID PWR Beacons #Data, #/s CH MB ENC CIPHER AUTH ESSID 00:34:34:34:34:34 -56 9 0 0 11 54e. WPA2 CCMP PSK wifi_id 00:44:44:44:44:34 -56 9 0 0 11 54e. WPA2 CCMP PSK wifi_id2 00:54:54:54:54:54 -56 9 0 0 11 54e. WPA2 CCMP PSK wifi_id3 

我想分割每行(包含在一个字符)以获得字段BSSID,CH,CIPHER和ESSID。 我最终的目标是将每一行的字段存储在一个字符组中,以更舒适地工作。 像这样的东西:

 char fields[] = { BSSID, CH,CIPHER, ESSID} 

现在我正在使用strtok ,为了分裂字符的\t ,但这非常不舒服。 在这里,这是我的第一个方法,但是很差,因为它只关注第四行和第二行。 任何人可以帮助我的代码? 我也打开了一个不同的编程方式。

 const char s[2]= "\t"; while (fgets(path, sizeof(path)-1, fp) != NULL) { i = i + 1; if (i == 4){ token = strtok(path, s); /* walk through other tokens */ while( token != NULL ) { token = strtok(NULL, s); strncpy(field2, token, 18); break; } } } 

你用strtok方法是好的,但也许你想要将数据存储到一个结构。 像下面这样。 我选择固定字符串的最大长度,并刚刚发明了那些可能的东西。

 struct row_data { char bssid[18]; char ch[4]; char cipher[10]; char essid[20]; }; 

如果您始终知道列的顺序,您可以在此停下来。 只需使用枚举对列进行索引:

 enum column_id { COL_RSSID = 0, COL_CH = 5, COL_CIPHER = 8, COL_ESSID = 10 }; 

现在这样的事情会做到这一点:

 int column = 0; char *target = NULL; struct row_data row; struct row_data empty_row = {0}; while( fgets(path, sizeof(path), fp) ) { row = empty_row; token = strtok(path, s); for( column = 0; token; token = strtok(NULL,s), column++ ) { switch( column ) { case COL_RSSID: target = row.rssid; break; case COL_CH: target = row.ch; break; case COL_CIPHER: target = row.cipher; break; case COL_ESSID: target = row.essid; break; default: target = NULL; } if( target ) strcpy(target, token); } /* do something with row */ printf( "Read rssid=%s ch=%s cipher=%s essid=%s\n", row.rssid, row.ch, row.cipher, row.essid ); } 

也没有太多额外的工作,也可以作为参数strncpy (我的例子只是简短,并使用strcpy )的target_length或类似的。 或者你可以走不同的方向,只在结构中存储指针。 然后你可以使用动态分配来复制字符串。

现在,如果你的列顺序是不知道的,你将不得不进一步抽象这一步。 这将首先读取标题行,并寻找你感兴趣的部分,并存储他们出现在列索引。 这会使你的代码更复杂,但不是不合理的。

一个起点可能是(需要<stdlib.h> ):

 struct column_map { const char * name; size_t offset; int index; } columns = { { "RSSID", offsetof( struct row_data, rssid ), -1 }, { "CH", offsetof( struct row_data, ch ), -1 }, { "CIPHER", offsetof( struct row_data, cipher ), -1 }, { "ESSID", offsetof( struct row_data, essid ), -1 }, { NULL } }; /* first read the header */ token = strtok(header, s); for( column = 0; token; token = strtok(NULL,s), column++ ) { for( struct column_map *map = columns; map->name; map++ ) { if( map->index == -1 && 0 == strcmp(token, map->name) ) { map->index = column; } } } 

你可以看到这是怎么回事 假设你已经将头部读入header ,现在你已经填充了列中每列感兴趣的列索引。所以当读取其他行时,你可以这样做,而不是使用开关:

 row = empty_row; token = strtok(path, s); for( column = 0; token; token = strtok(NULL,s), column++ ) { for( struct column_map *map = columns; map->name; map++ ) { if( map->index == column ) { /* again, if using strncpy, store a length inside the map, and use MIN(map->length, strlen(token)+1) or similar */ memcpy( (char*)&row + map->offset, token, strlen(token) ); } } } 

不用在表格中存储偏移量,你当然可以存储一个指针,就像我们在switch语句中用target所做的那样。 但是这需要直接指向像&row.rssid这样的东西。 也许这对你已经足够了(我怀疑我已经提供了足够多的)。

但是为了公平起见,我会指出这个选项,这可能比使用上面的memcpy更简单。 而且我会卷起我一直避免的那些东西。

 struct row_data row; struct column_map { const char * name; char *target; size_t target_size; int index; } columns = { { "RSSID", row.rssid, sizeof(row.rssid), -1 }, { "CH", row.ch, sizeof(row.ch), -1 }, { "CIPHER", row.cipher, sizeof(row.cipher), -1 }, { "ESSID", row.essid, sizeof(row.essid), -1 }, { NULL } }; /* ::: */ if( map->index == column ) { strncpy( map->target, token, map->target_size ); map->target[map->target_size-1] = '\0'; /* in case of overflow */ } 

一个简单的技巧:

考虑到你的“单词”没有任何空格,你可以使用sscanf

这个函数将允许你从字符串而不是stdin读取值。 如果它们之间有任何空格,这些将自动分析为单独的值。 你可以忽略你不想读的值。

例:

 sscanf(token, "%s %*s %*s %*s %*s %s %*s %*s %s %*s %s",BSSID, CH, CIPHER, ESSID); 

%*s将读取一个字段,但不会将其分配给任何变量。 所以,只有必要的字段将被分配给变量。

您必须为输出中的每一行运行此语句。