为什么在C语言中strcpy时编码搞乱了

Has*_*haf 2 c file character-encoding

源代码:

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();
}
Run Code Online (Sandbox Code Playgroud)

执行样本:

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!
Run Code Online (Sandbox Code Playgroud)

数据文件(之前):

[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]
Run Code Online (Sandbox Code Playgroud)

数据文件(后):

[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???]
Run Code Online (Sandbox Code Playgroud)

问题:

正如您所看到的,问题是付款日期和电话号码字段搞砸了.它发生在我使用后strcpy.我调试了这个read_string(..)功能,看起来很好.我不明白为什么会这样.任何帮助将非常感谢解决这个问题.

有趣的部分:只有datephone_number受到影响.name,password,billing_address没有TIS问题.

klu*_*utt 5

这是指针和数组不同的一个非常好的例子:

char* read_string(int length) {
    char data[length];
    // code
    return data;
}
Run Code Online (Sandbox Code Playgroud)

将无法工作,因为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);
Run Code Online (Sandbox Code Playgroud)

解决这个问题的一种方法是使用char data* = malloc(length * sizeof *data).这样您就可以使用以前的读取.但总的来说,你想避免malloc像这样隐藏,因为你free之后需要它们.如果您想寻求解决方案,请执行以下操作:

char * read_string(char * dest, int length) {
    char data[length];
    // code
    return strncpy(dest, data, length);
}
Run Code Online (Sandbox Code Playgroud)

然后像这样调用它:

char * str = malloc(length);
if(! read_string(str, length)) {
    fprintf(stderr, "Error reading string\n");
    exit(EXIT_FAILURE);
}
// Code
free(str);
Run Code Online (Sandbox Code Playgroud)