Hai vấn đề xung quanh câu hỏi này là quản lý bộ nhớ và an toàn luồng. Như bạn có thể thấy từ rất nhiều bài đăng, đây không phải là một nhiệm vụ dễ dàng để thực hiện hoàn hảo trong C. Tôi muốn một giải pháp đó là:
- Chủ đề an toàn. (strtok không phải là chủ đề an toàn)
- Không sử dụng malloc hoặc bất kỳ dẫn xuất nào của nó (để tránh các vấn đề về quản lý bộ nhớ)
- Kiểm tra giới hạn mảng trên các trường riêng lẻ (để tránh lỗi phân đoạn trên dữ liệu không xác định)
- Hoạt động với các dấu tách trường nhiều byte (utf-8)
- bỏ qua các trường bổ sung trong đầu vào
- cung cấp thói quen lỗi mềm cho độ dài trường không hợp lệ
Giải pháp tôi đưa ra đáp ứng tất cả các tiêu chí này. Có thể cần thêm một chút công việc để thiết lập so với một số giải pháp khác được đăng ở đây, nhưng tôi nghĩ rằng trong thực tế, công việc bổ sung đáng giá để tránh những cạm bẫy chung của các giải pháp khác.
#include <stdio.h>
#include <string.h>
struct splitFieldType {
char *field;
int maxLength;
};
typedef struct splitFieldType splitField;
int strsplit(splitField *fields, int expected, const char *input, const char *fieldSeparator, void (*softError)(int fieldNumber,int expected,int actual)) {
int i;
int fieldSeparatorLen=strlen(fieldSeparator);
const char *tNext, *tLast=input;
for (i=0; i<expected && (tNext=strstr(tLast, fieldSeparator))!=NULL; ++i) {
int len=tNext-tLast;
if (len>=fields[i].maxLength) {
softError(i,fields[i].maxLength-1,len);
len=fields[i].maxLength-1;
}
fields[i].field[len]=0;
strncpy(fields[i].field,tLast,len);
tLast=tNext+fieldSeparatorLen;
}
if (i<expected) {
if (strlen(tLast)>fields[i].maxLength) {
softError(i,fields[i].maxLength,strlen(tLast));
} else {
strcpy(fields[i].field,tLast);
}
return i+1;
} else {
return i;
}
}
void monthSplitSoftError(int fieldNumber, int expected, int actual) {
fprintf(stderr,"monthSplit: input field #%d is %d bytes, expected %d bytes\n",fieldNumber+1,actual,expected);
}
int main() {
const char *fieldSeparator=",";
const char *input="JAN,FEB,MAR,APRI,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,FOO,BAR";
struct monthFieldsType {
char field1[4];
char field2[4];
char field3[4];
char field4[4];
char field5[4];
char field6[4];
char field7[4];
char field8[4];
char field9[4];
char field10[4];
char field11[4];
char field12[4];
} monthFields;
splitField inputFields[12] = {
{monthFields.field1, sizeof(monthFields.field1)},
{monthFields.field2, sizeof(monthFields.field2)},
{monthFields.field3, sizeof(monthFields.field3)},
{monthFields.field4, sizeof(monthFields.field4)},
{monthFields.field5, sizeof(monthFields.field5)},
{monthFields.field6, sizeof(monthFields.field6)},
{monthFields.field7, sizeof(monthFields.field7)},
{monthFields.field8, sizeof(monthFields.field8)},
{monthFields.field9, sizeof(monthFields.field9)},
{monthFields.field10, sizeof(monthFields.field10)},
{monthFields.field11, sizeof(monthFields.field11)},
{monthFields.field12, sizeof(monthFields.field12)}
};
int expected=sizeof(inputFields)/sizeof(splitField);
printf("input data: %s\n", input);
printf("expecting %d fields\n",expected);
int ct=strsplit(inputFields, expected, input, fieldSeparator, monthSplitSoftError);
if (ct!=expected) {
printf("string split %d fields, expected %d\n", ct,expected);
}
for (int i=0;i<expected;++i) {
printf("field %d: %s\n",i+1,inputFields[i].field);
}
printf("\n");
printf("Direct structure access, field 10: %s", monthFields.field10);
}
Dưới đây là một ví dụ biên dịch và đầu ra. Lưu ý rằng trong ví dụ của tôi, tôi cố tình đánh vần "APRIL" để bạn có thể thấy lỗi mềm hoạt động như thế nào.
$ gcc strsplitExample.c && ./a.out
input data: JAN,FEB,MAR,APRIL,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC,FOO,BAR
expecting 12 fields
monthSplit: input field #4 is 5 bytes, expected 3 bytes
field 1: JAN
field 2: FEB
field 3: MAR
field 4: APR
field 5: MAY
field 6: JUN
field 7: JUL
field 8: AUG
field 9: SEP
field 10: OCT
field 11: NOV
field 12: DEC
Direct structure access, field 10: OCT
Thưởng thức!
strtok
chức năng từ thư viện tiêu chuẩn để đạt được điều tương tự.