C Обработка файлов и сопоставление справки - PullRequest
1 голос
/ 09 февраля 2011

У меня есть задание в колледже, в котором мы должны прочитать два файла .dat (aascii), которые мы создали с помощью более ранней программы, оба из которых отсортированы.

Один - это файл учетной записи клиента, который содержит баланс клиентов, номер и имя счета, другой - файл транзакций, который содержит номер счета и транзакции для этой учетной записи.

Эта программа должна соответствовать номерам счетов и создавать новый и обновленный файл клиентов, добавляя сумму транзакции к балансу клиента на основе номера счета.

У меня это нормально работает, за исключением случаев, когда есть дублирующаяся транзакция, например, если файл транзакции содержит 2 отдельные транзакции для 2-го клиента, мой код напечатает обновленный баланс после обеих транзакций вместо кумулятивного баланса.

Мне просто интересно, сможет ли кто-нибудь пролить свет на решение этой проблемы. Мой код прилагается, заранее спасибо.

#include <stdio.h>
#include <conio.h>
int main()
{ 
    int account,matches=0;     /* account number */
    char date[ 30 ]; /* account Date */
    double balance, saleamount,total=0, x;  /* account SaleAmount */
    int transaccount;
    char name [ 30 ];



    FILE *cfPtr;     /* cfPtr = clients.dat file pointer */
    FILE *ctPtr;    /* cfPtr = transaction.dat file pointer */
    FILE *cfPtr2;    /* cfPtr2 = new client file */

    cfPtr2 = fopen( "clientupdate.dat", "w" );

    /* fopen opens file; exits program if file cannot be opened */ 
    if ( ( cfPtr = fopen( "clients.dat", "r" ) ) == NULL ) {
        printf( "clients could not be opened\n" );
        fflush(stdin);
    } /* end if */
    else
        if( ( ctPtr = fopen( "transactions.dat", "r" ) ) == NULL)
        {
            printf( "File could not be opened\n" );
            fflush(stdin);
        }
    else { /* read account, date,name, balance and SaleAmount from files */
        fscanf( cfPtr, "%d%s%lf", &account, &name, &balance );
        fflush(stdin);

        fscanf( ctPtr, "%d%s%lf", &transaccount, &date, &saleamount );
        fflush(stdin);

        printf( "%-13s%-10s%s\n", "  Account", "Name", "Balance" );
        printf("|----------------------------------|\n");
        while( !feof(ctPtr))
        {   

            while( !feof(cfPtr) &&matches==0 )
            {   

                if(account == transaccount)
                {
                   matches=1;
                    total=0;
                    x = balance+saleamount;
                    total = total + x;
                    balance = total; 

                printf("  %-10d%-10s%.2lf\n", account, name, total);
                }
                else
                {

                    fscanf( cfPtr, "%d%s%lf", &account, &name, &balance );
                    fflush(stdin);
                }   

            }
            fprintf( cfPtr2, "%d %s %.2lf\n", account, name, total );
            fscanf( ctPtr, "%d%s%lf", &transaccount, &date, &saleamount );
            fflush(stdin);
           matches=0;

        }

        fclose( cfPtr2 );
        getch();
}

Ответы [ 4 ]

4 голосов
/ 09 февраля 2011

Хотя я не могу поверить, что они на самом деле все еще преподают Кобол (под маской Си), я помогу вам, поскольку, в отличие от тех, кто обращается за помощью, вы фактически добавили немало работа: -)

Хитрость заключается в том, чтобы не печатать после каждого обновления, а подождать, пока номер счета не изменится, распечатать предыдущие окончательные сведения.

И, поскольку вы достигнете конца файла транзакции при обработке последней учетной записи, вам придется сделать это еще раз после выхода из цикла.

Вот подробные шаги.


Введите еще три переменные, char lastname[30], int lastaccount и double lastbalance. Убедитесь, что lastaccount инициализирован на -1.

Тогда ваш код может поймать переходы номера счета, просто проверив значения lastaccount и account. Хранение всех трех полей обеспечивает распечатку сведений о предыдущей учетной записи, даже если вы уже прочитали сведения о новой учетной записи.


Перед matches = 1 вставить следующий код:

if (lastaccount != account) {
    if (lastaccount != -1)
        printf("  %-10d%-10s%.2lf\n", lastaccount, lastname, lastbalance);
    lastaccount = account;
    strcpy (lastname, name);
}

Вам также нужно будет включить string.h, чтобы получить прототип для strcpy.

Это отлавливает переходы номера счета и распечатывает данные о последнем (если, конечно, последний не был номером счета -1, конечно, поскольку это означает, что - это предыдущего). Но независимо от того, печатает он или нет, он настроен на следующий переход.


Сразу после balance = total добавить lastbalance = balance;. Это сохранит самый последний баланс текущего счета, чтобы его можно было распечатать при переходе.

В отличие от номера и имени счета, это необходимо делать для каждой транзакции , а не для каждой учетной записи . Это связано с тем, что учетная запись / имя для учетной записи не изменяются, а для конечного баланса (для каждой транзакции).


За пределами цикла, непосредственно перед fclose (cfPtr2);, введите код:

if (lastaccount != -1)
    printf("  %-10d%-10s%.2lf\n", lastaccount, lastname, lastbalance);

Это распечатывает детали для окончательного счета, предполагая, что они были обработаны вообще. Как упоминалось ранее, это необходимо, потому что вы достигнете конца файла, не выполнив переход из-за измененного номера учетной записи.


И, пожалуйста, в качестве окончательного запроса:

  • избавьтесь от conio.h и используйте getchar() вместо getch().
  • , если возможно, ditch Turbo C и использовать gcc - это , так что намного лучше: -)
  • не fflush(stdin).
  • использовать канонический int main (void) ....
  • вернуть что-то из main (хотя я думаю, что это ослаблено в последующих версиях стандарта).

Я не буду публиковать свой действительный код (если вы не гарантируете, что это не домашняя работа), но следующая расшифровка показывает, что этих изменений должно быть достаточно, чтобы исправить ваши текущие проблемы:

$ cat clients.dat
1 Pax 4.2
2 Roger 0

$ cat transactions.dat
1 1/2/3 112.3
1 2/3/4 -22.1
2 3/4/5 7

$ ./qq
  Account    Name      Balance
|----------------------------------|
  1         Pax       94.40
  2         Roger     7.00

Что бы это ни стоило, я не уверен, что полностью согласен с трактовкой файла трансакции как основного элемента. Поскольку ваша задача - скопировать файл учетной записи с необходимыми изменениями (как указано в файле транзакции), я бы предпочел обработать файл учетной записи как основной блок.

Мне так кажется чище.

И, в целях полноты, вот код, который я придумал. Я бы не советовал сдавать это как задание, поскольку есть множество вещей, которые могут снизить вашу оценку, вещи, которые гораздо менее значимы в реальном мире, но о которых у преподавателей есть особая пчела :-) несколько точек возврата, используя #define вместо enum и т. д.

#include <stdio.h>
#include <string.h>

#define HDR_LINE 0
#define HDR_FULL 1

static void outHeader (int withHeading) {
    printf ("+-%-6s-+-%-10s-+-%-6s-+-%-6s-+-%-6s-+\n",
        "------", "----------", "------", "------", "------");
    if (withHeading)
        printf ("| %-6s | %-10s | %-6s | %-6s | %-6s |\n",
            " Acct ", "   Name   ", " Date ", "Amount", "Balnce");
}

static void closeFiles (FILE *pfCli, FILE *pfTxn, FILE *pfNew) {
    if (pfCli) fclose (pfCli);
    if (pfTxn) fclose (pfTxn);
    if (pfNew) fclose (pfNew);
}

static int openFiles (FILE **pfCli, FILE **pfTxn, FILE **pfNew) {
    *pfCli = *pfTxn = *pfNew = NULL;

    if ((*pfCli = fopen ("clients.dat", "r")) == NULL) {
        printf ("File clients.dat could not be opened\n");
        closeFiles (*pfCli, *pfTxn, *pfNew);
        return 0;
    }

    if ((*pfTxn = fopen ("transactions.dat", "r")) == NULL) {
        printf ("File transactions.dat could not be opened\n");
        closeFiles (*pfCli, *pfTxn, *pfNew);
        return 0;
    }

    if ((*pfNew = fopen ("clientupdate.dat", "w")) == NULL) {
        printf ("File clientupdate.dat could not be opened\n");
        closeFiles (*pfCli, *pfTxn, *pfNew);
        return 0;
    }

    return 1;
}

static void outCli (int acct, char *nm, double bal, char *ind) {
    if (acct == -1)
        printf ("| %6s | %-10s | %6s | %6s | %6.2lf | %s\n",
            "", nm, "", "", bal, ind);
    else
        printf ("| %6d | %-10s | %6s | %6s | %6.2lf | %s\n",
            acct, nm, "", "", bal, ind);
}

static void outTxn (char *date, double amt, double bal) {
    printf ("| %6s | %10s | %-6s | %6.2lf | %6.2lf |\n",
        "", "", date, amt, bal);
}

static void getTxn (FILE *fh, int *acct, char *date, double *amt) {
    if (feof (fh))
        *acct = -1;
    else
        fscanf (fh, "%d%s%lf\n", acct, date, amt);
}

static void getCli (FILE *fh, int *last, int *acct, char *nm, double *bal) {
    if (*last != -1)
        outCli (-1, "", *bal, "-->");
    *last = *acct;
    fscanf (fh, "%d%s%lf\n", acct, nm, bal);
}

int main (void) { 
    int cli_acct, txn_acct, last_acct = -1;
    char txn_date[10], cli_nm[10];
    double cli_bal, txn_amt;
    FILE *fCli, *fTxn, *fNew;

    // Open all files, output header and load up first transaction.

    if (!openFiles (&fCli, &fTxn, &fNew))
        return 1;

    outHeader (HDR_FULL);

    getTxn (fTxn, &txn_acct, txn_date, &txn_amt);

    // Process every account.

    while (!feof (fCli)) {
        // Bring in an account and print starting balance.

        getCli (fCli, &last_acct, &cli_acct, cli_nm, &cli_bal);
        outHeader (HDR_LINE);
        outCli (cli_acct, cli_nm, cli_bal, "<--");

        // While account is not yet up to txn, output and read new.

        while (cli_acct < txn_acct) {
            fprintf (fNew, "%d %s %.2lf\n", cli_acct, cli_nm, cli_bal);
            outCli (-1, "", cli_bal, "-->");
            outHeader (HDR_LINE);
            fscanf (fCli, "%d%s%lf\n", &cli_acct, &cli_nm, &cli_bal);
            outCli (cli_acct, cli_nm, cli_bal, "<--");
        }

        // If they don't match, you have a orphan transaction.

        if ((cli_acct != txn_acct) && (txn_acct != -1)) {
            printf ("Orphan transaction for account # %d\n", txn_acct);
            closeFiles (fCli, fTxn, fNew);
            return 1;
        }

        // While account and transaction match, process transaction lines.

        while (cli_acct == txn_acct) {
            cli_bal = cli_bal + txn_amt;
            outTxn (txn_date, txn_amt, cli_bal);
            if (feof (fTxn))
                txn_acct = -1;
            else
                fscanf (fTxn, "%d%s%lf\n", &txn_acct, &txn_date, &txn_amt);
        }
        fprintf (fNew, "%d %s %.2lf\n", cli_acct, cli_nm, cli_bal);
    }

    // Output final lines if needed.

    if (last_acct != -1)
        outCli (-1, "", cli_bal, "-->");
    outHeader (HDR_LINE);

    // Close all files and exit successfully.

    closeFiles (fCli, fTxn, fNew);
    return 0;
}

Учитывая clients.dat из:

0 Mary 99.9
1 Pax 4.2
2 Bob 7
4 Roger 0
5 Nobody 9

и transactions.dat из:

1 1/2/3 112.3
1 2/3/4 -22.1
4 3/4/5 7

это выводит:

+--------+------------+--------+--------+--------+
|  Acct  |    Name    |  Date  | Amount | Balnce |
+--------+------------+--------+--------+--------+
|      0 | Mary       |        |        |  99.90 | <--
|        |            |        |        |  99.90 | -->
+--------+------------+--------+--------+--------+
|      1 | Pax        |        |        |   4.20 | <--
|        |            | 1/2/3  | 112.30 | 116.50 |
|        |            | 2/3/4  | -22.10 |  94.40 |
|        |            |        |        |  94.40 | -->
+--------+------------+--------+--------+--------+
|      2 | Bob        |        |        |   7.00 | <--
|        |            |        |        |   7.00 | -->
+--------+------------+--------+--------+--------+
|      4 | Roger      |        |        |   0.00 | <--
|        |            | 3/4/5  |   7.00 |   7.00 |
|        |            |        |        |   7.00 | -->
+--------+------------+--------+--------+--------+
|      5 | Nobody     |        |        |   9.00 | <--
|        |            |        |        |   9.00 | -->
+--------+------------+--------+--------+--------+

и создает clientupdate.dat из:

0 Mary 99.90
1 Pax 94.40
2 Bob 7.00
4 Roger 7.00
5 Nobody 9.00

Не стесняйтесь использовать этот код для любых целей, кроме как выдавать его за домашнее задание.

0 голосов
/ 10 февраля 2011

С помощью pax и хорошей игры с моим собственным кодом я разработал это рабочее решение. Еще раз спасибо за помощь, очень признателен.

#include <stdio.h>
#include <string.h>
int main(void)
{ 
    int account;     /* account number */
    char date[ 30 ]; /* account Date */
    double balance, saleamount; /* account SaleAmount */
    int transaccount;
    char name [ 30 ];
    int newaccount;
    char newname[ 30];
    double newbalance;
    int match=0;



    FILE *cfPtr;     /* cfPtr = clients.dat file pointer */
    FILE *ctPtr;    /* cfPtr = transaction.dat file pointer */
    FILE *cfPtr2;    /* cfPtr2 = new client file */

    cfPtr2 = fopen( "clientupdate.dat", "w" );

    /* fopen opens file; exits program if file cannot be opened */ 
    if ( ( cfPtr = fopen( "clients.dat", "r" ) ) == NULL ) {
        printf( "clients could not be opened\n" );

    } /* end if */
    else
        if( ( ctPtr = fopen( "transactions.dat", "r" ) ) == NULL)
        {
            printf( "File could not be opened\n" );

        }
    else { /* read account, date,name, balance and SaleAmount from files */
        fscanf( cfPtr, "%d%s%lf", &account, &name, &balance );


        fscanf( ctPtr, "%d%s%lf", &transaccount, &date, &saleamount );
        fscanf( cfPtr2, "%d%s%lf", &newaccount, &newname, &newbalance );
        while ( !feof( cfPtr ) )
        {
            transaccount=1;
            newbalance=balance;
            while ( !feof( ctPtr ) )
            {
                if(transaccount==account&&match==0)
                {
                 newbalance = balance + saleamount;
                 match++;
                }
                else
                    if(transaccount==account&&match>0)
                     newbalance = newbalance + saleamount;

                fscanf( ctPtr, "%d%s%lf", &transaccount, date, &saleamount );
            }
            rewind( ctPtr);
            match=0;

        fprintf( cfPtr2, "%d %s %.2f\n", account, name, newbalance );
        fscanf( cfPtr, "%d%s%lf", &account, name, &balance );
        }

        rewind( cfPtr2);
    }
        fclose( cfPtr2 );
        fclose( cfPtr );
        fclose( ctPtr );
        getchar();
    return 0;
}
0 голосов
/ 09 февраля 2011

Я не уверен, что проблема, с которой вы сталкивались, связана, но:

  1. fflush(stdin) дает неопределенное поведение - не делайте этого.Если вы хотите пропустить все до следующей новой строки, вы можете сделать то, что с scanf("%*[^\n'");
  2. while( !feof(ctPtr)) почти гарантированно вызовет проблемы - это обычно делает его похожим на последнее чтениеиз файла происходит дважды, дублируя данные.

«Двойная транзакция» звучит так, как будто она, вероятно, связана с последним.

0 голосов
/ 09 февраля 2011

Вместо вывода результатов с помощью printf сразу, поместите результаты в массив.

Как только вы закончили работу с циклами while, используйте циклы for для просмотра массива хранимых данных, поиска дублирующих транзакций и объединения их вместе для вывода объединенных транзакций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...