源代码:
char CUSTOMERS_FILE[50] = "customers.txt";
typedef struct Customer {
char name[50];
char password[50];
char billing_address[100];
char phone_number[15];
double amount_paid;
double amount_due;
char date[20];
} Customer;
char* read_string(int length) {
char data[length];
rewind(stdin);
fgets(data, length, stdin);
if (data[0] == '\n') {
data[0] = '\0';
}
strtok(data, "\n");
printf("DATA: %s", data);
return data;
}
void handle_modify_customer(Customer customer) {
Customer edited_details;
printf("\nMODIFYING DETAILS\n==============\n\n");
printf("CREATE A CUSTOMER PROFILE\n=========================\n");
printf("Name (%s): ", customer.name);
strcpy(edited_details.name, read_string(50));
printf("Password (%s): ", customer.password);
strcpy(edited_details.password, read_string(50));
printf("Billing Address (%s): ", customer.billing_address);
strcpy(edited_details.billing_address, read_string(100));
printf("Phone Number (%s): ", customer.phone_number);
strcpy(edited_details.phone_number, read_string(15));
printf("Amount Paid (%10.2lf): ", customer.amount_paid);
scanf("%lf", &edited_details.amount_paid);
printf("Amount Due (%10.2lf): ", customer.amount_due);
scanf("%lf", &edited_details.amount_due);
printf("Payment Date (%s): ", customer.date);
strcpy(edited_details.date, read_string(20));
/*
if (strlen(edited_details.name) == '\0' || strlen(edited_details.billing_address) == '\0' || strlen(edited_details.password) == '\0' || strlen(edited_details.phone_number) == '\0' || strlen(edited_details.date) == '\0') {
printf("All fields must be filled in!");
handle_modify_customer(customer);
}*/
if (edited_details.name[0] == '\0' || edited_details.billing_address[0] == '\0' || edited_details.password[0] == '\0' || edited_details.phone_number[0] == '\0' || edited_details.date[0] == '\0') {
printf("All fields must be filled in!");
handle_modify_customer(customer);
}
FILE *file = fopen(CUSTOMERS_FILE, "r");
FILE *new_file = fopen("customers_new.txt", "ab+");
Customer record;
while (fscanf(file, "[%[^]]], [%[^]]], [%[^]]], [%[^]]], [%lf], [%lf], [%[^]]]\n",
record.name, record.password, record.billing_address, record.phone_number,
&record.amount_paid, &record.amount_due, record.date) == 7)
{
if (strcmp(customer.name, record.name) == 0) {
printf("P: %s\nD: %s", edited_details.phone_number, edited_details.date);
fprintf(new_file, "[%s], [%s], [%s], [%s], [%lf], [%lf], [%s]\n", edited_details.name, edited_details.password, edited_details.billing_address, edited_details.phone_number, edited_details.amount_paid, edited_details.amount_due, edited_details.date);
} else {
fprintf(new_file, "[%s], [%s], [%s], [%s], [%lf], [%lf], [%s]\n", record.name, record.password, record.billing_address, record.phone_number, record.amount_paid, record.amount_due, record.date);
}
}
fclose(file);
fclose(new_file);
remove(CUSTOMERS_FILE);
rename("customers_new.txt", CUSTOMERS_FILE);
printf("\nThe customer details have been successfully modified!\n");
key_to_continue();
}
执行示例:
MODIFYING DETAILS
==============
CREATE A CUSTOMER PROFILE
=========================
Name (dumbfk): test
DATA: testPassword (abc123): lol
DATA: lolBilling Address (pukima jalan): lol
DATA: lolPhone Number (6969696969): 499449
DATA: 499449Amount Paid ( 6969.00): 499449
Amount Due (6969699.00): 499494
Payment Date (6/9/1969): 22/2/2000
DATA: 22/2/2000P: �O���
D: �O���
The customer details have been successfully modified!
数据文件(之前):
[well lol], [abc123], [wtf bro? 24], [0183188383], [3000.000000], [4000.000000], [12/12/2012]
[chow hai], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
[lol head], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
[stupid face], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
[dumbfk], [abc123], [pukima jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
数据文件(之后):
[well lol], [abc123], [wtf bro? 24], [0183188383], [3000.000000], [4000.000000], [12/12/2012]
[chow hai], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
[lol head], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
[stupid face], [abc123], [lol jalan], [6969696969], [6969.000000], [6969699.000000], [6/9/1969]
[test], [lol], [lol], [�O���], [499449.000000], [499494.000000], [�O���]
问题:
您可能会看到,问题在于“付款日期”和“电话号码”字段混乱了。我使用strcpy
之后就发生了。我调试了read_string(..)
函数,这似乎很好。我不明白为什么会这样。对于解决此问题的任何帮助将不胜感激。
有趣的部分:仅date
和phone_number
受到影响。 name
,password
,billing_address
没问题。
答案 0 :(得分:5)
这是一个很好的例子,说明指针和数组不是同一件事:
char* read_string(int length) {
char data[length];
// code
return data;
}
将不起作用,因为data
是在堆栈上分配的本地数组,并且在函数返回时将不复存在。
如果将char data[length]
更改为static char data[length]
,它将起作用。但是请注意,先前的读取将被覆盖,因此这将无法按预期进行:
char *s1, *s2;
s1 = read_string(10);
s2 = read_string(10);
printf("First string: %s\n", s1);
printf("Second string: %s\n", s2);
一种解决方法是使用char data* = malloc(length * sizeof *data)
。这样,您将能够使用以前的读物。但总的来说,您要避免这样的隐藏malloc
,因为之后需要free
。如果您想寻求该解决方案,请执行以下操作:
char * read_string(char * dest, int length) {
char data[length];
// code
return strncpy(dest, data, length);
}
然后这样称呼它:
char * str = malloc(length);
if(! read_string(str, length)) {
fprintf(stderr, "Error reading string\n");
exit(EXIT_FAILURE);
}
// Code
free(str);
答案 1 :(得分:2)
将read_string函数中的本地char数据更改为静态变量,以免丢失内存
#define MAX_STR_LENGTH 100
char* read_string(int length)
{
static char data[MAX_STR_LENGTH]; // change to static -- it will not change
rewind(stdin);
fgets(data, length, stdin);
if (data[0] == '\n') {
data[0] = '\0';
}
strtok(data, "\n");
printf("DATA: %s", data);
return data;
}
答案 2 :(得分:1)
另一个选择是将指针与长度一起传递。
void read_string(char *data, int length) {
fgets(data, length, stdin);
if (data[0] == '\n') {
data[0] = '\0';
}
strtok(data, "\n");
printf("DATA: %s", data);
}
致电
read_string(edited_details.name, 50);
那么就不需要使用strcpy()
答案 3 :(得分:-1)
C语言中不直接支持字符串。大多数时候,您必须处理(我要说“内存块”而不是“数组”吗?)包含字符和终止符NUL
的内存块最后。
在您的情况下,它是这样的:
您将文件中的字符串(一定数量的字符)读入 大量的内存
您希望那部分内存在直到 您使用了字符串
您使用您的字符串。就是这样。
数字2是您的代码中损坏的内容。
char* read_string(int length) {
/* "data" is your chunk of memory.
* It is an array.
* This array is local to containing function.
* This array is automatically allocated on the stack.
* This array has its address in memory. Like &data[0].
* The address may change from one invocation of the function to another.
*/
char data[length];
/* some code here... */
return data; /* true, this returns the address &data[0] */
}
/* The function is done. Local variables (array by the name of "data") are gone.
* The memory on the stack that was once allocated for local variables is
* considered "free-to-use" by any other function you will invoke next. This memory
* is not valid for use outside the function any more, but you may and you do
* return a pointer to that memory, which is dangerous to use and harmful
*/
基本上有三种方法可以解决此问题:
您可以声明一个全局内存块,例如一个全局数组:
char data[MAX_POSSIBLE_LENGTH]; /* Static storage. Global scope. */
char* read_string() {
/* some code here... */
return data;
}
...或使数组具有固定长度的静态数组:
char* read_string() {
static char data[MAX_POSSIBLE_LENGTH]; /* Static storage. Function scope. */
/* some code here... */
return data;
}
...或从外部将数组及其长度传递进来:
char* read_string(char *data, int length) {
/* some code here... */
return data;
}
...或使用动态分配
char* read_string(int length) {
/* some code here... */
char *data;
data = malloc(length); /* Dynamic storage. Must be freed somewhere./*
/* This is not an "array", now this really is a "chunk of memory" :) */
return data;
}
void handle_modify_customer(Customer customer)
{
Customer edited_details;
char * input_str_ptr;
printf("\nMODIFYING DETAILS\n==============\n\n");
printf("CREATE A CUSTOMER PROFILE\n=========================\n");
printf("Name (%s): ", customer.name);
strcpy(edited_details.name, input_str_ptr = read_string(50));
free(input_str_ptr); /* Don't allow mem leakage */
/* ... */
}