2015-11-06 15 views
6

Ödevim için, fread/fwrite kullanmam gerekiyor. Benfread() a içinde bir yapı c

yazdım
#include <stdio.h> 
#include <string.h> 

struct rec{ 
    int account; 
    char name[100]; 
    double balance; 
}; 

int main() 
{ 
    struct rec rec1; 
    int c; 

    FILE *fptr; 
    fptr = fopen("clients.txt", "r"); 

    if (fptr == NULL) 
     printf("File could not be opened, exiting program.\n"); 
    else 
    { 
     printf("%-10s%-13s%s\n", "Account", "Name", "Balance"); 
     while (!feof(fptr)) 
     { 
      //fscanf(fptr, "%d%s%lf", &rec.account, rec.name, &rec.balance); 
      fread(&rec1, sizeof(rec1),1, fptr); 
      printf("%d %s %f\n", rec1.account, rec1.name, rec1.balance); 
     } 
     fclose(fptr); 
    } 
    return 0; 
} 

clients.txt dosyası

 
100 Jones 564.90 
200 Rita 54.23 
300 Richard -45.00 

çıkış

 
Account Name   Balance 
540028977 Jones 564.90 
200 Rita 54.23 
300 Richard -45.00╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠ 
╠╠ü☻§9x°é -92559631349317831000000000000000000000000000000000000000000000.000000 

Press any key to continue . . . 

ben fscanf (Ive dışarı yorumladı olan) ile yapabilirsiniz, ama kullanmak için gerekli ediyorum fread/fwrite.

  1. Jone'un hesabı için neden büyük bir sayı ile başlıyor?
  2. Neden çöp var? Bunu durdurmak zorunda değil mi?
  3. Bu yöntem kullanılarak herhangi bir sakınca var mı? veya fscanf yöntemi?

Bunları nasıl düzeltebilirim? Çok teşekkürler,

+4

Lütfen "Neden" while (! Feof (dosya)) "her zaman yanlış mı?" (Http://stackoverflow.com/q/5431941/2173917) –

+0

Lütfen okuyunuz Neden [while is while (! Feof (fptr))) 'her zaman yanlış] (http://stackoverflow.com/a/26557243/1983495). –

+4

fread, ham ikiliyi okur. Clients.txt bir metin dosyasıdır, bu yüzden bu dosyayı ham ikili veri olarak okuyan bir nokta yoktur. –

cevap

4

Yorumlar derken, fread, dosyanızdaki baytları herhangi bir yorum yapmadan okur. Dosya clients.txt, ilk satırda 16, ikinci satırda 18, üçüncü satırda 14, artı iki satır karakterinde 50 karakterden oluşur. (Üçüncü satırdan sonra, istemciler.txt, yakında göreceğiniz gibi üçüncü satır içermez.) Yeni satır karakteri, UNIX veya Mac OS X makinelerinde tek bir bayt \n, ancak (muhtemelen) Windows makinelerinde \r\n iki bayttır. 50 veya 51 karakter.

3130 3020 4a6f 6e65 7320 3536 342e 3930  100 Jones 564.90 
0a32 3030 2052 6974 6120 3534 2e32 330a  \n200 Rita 54.23\n 
3330 3020 5269 6368 6172 6420 2d34 352e  300 Richard -45. 
3030          00 

Sizin fread açıklamada kopyalar doğrudan rec1 veri yapısına hiçbir yorum yapılmadan bu bayt: Burada onaltılık ASCII bayt dizisidir. Bu yapı, ilk dört baytı int olarak yorumlamak için int account; ile başlar. Belirtilen yorumlardan biri olarak, programınızı küçük bir makinede (büyük ihtimalle bir Intel makinesi) çalıştırıyorsunuz, bu yüzden en az anlamlı bayt ilk ve en önemli bayt dördüncü. Böylece, fread, dört ASCII karakterinin sırasını, 0x20303031 dört bayt tamsayı olarak, ondalık, 540028977 olarak eşit olarak yorumlamak için söyledi. Yapınızın bir sonraki üyesi char name[100];, yani rec1'daki sonraki 100 baytlık veri name olacaktır. Ancak fread'a sizeof(rec1)=112 bayt (4 baytlık hesap, 100 bayt isim, 8 baytlık balans) okuması söylendi. Dosyanız yalnızca 50 (veya 52) karakter olduğu için, fread yalnızca rec1'un çok sayıda baytını doldurabilecektir. fread'un geri dönüş değeri, onu atmamış olsaydınız, okumanın talep ettiğiniz bayt sayısının az olduğunu söylemiş olurdu. EOF'a bastığınız için, feof çağrısı, ilk geçişten sonra tüm dosyayı bir yudumda harcayarak döngüden çıktı.

Tüm çıktılarınız, yalnızca fprintf numaralı aramaya göre üretildi. 540028977 sayısı ve aşağıdaki alan "%d " ve rec1.account bağımsız değişkeni tarafından üretilmiştir. Sonraki bit sadece kısmen belirlenir ve şansınız vardır: "%s" belirteci ve ilgili rec1.name bağımsız değişkeni, \0 bayt bulunana kadar sonraki karakterleri ASCII olarak basacaktır.(Veya herhangi bir metin dosyası olarak), hangi anlamı dosyanızda hiç \0 bayt olduğundan, sonsuza devam potansiyel ve - iki yeni satır dahil - Böylece çıktı dosyanızın 50-4 (veya 52-4) kalan karakterler ile başlayacak bu dosyanızın son karakterini yazdırma sonra, ne görmeye ne olursa olsun çöp programınızı başladığında otomatik değişken rec1 olması oldu olduğunu. (Kasıtsız çıktı Bu tür OpenSSL'deki ünlü Heartbleed hata benzer.) Pisliğin yalnızca birkaç düzine daha karakterden sonra bir \0 byte dahil şanslıyız. printf'un, rec1.name'un yalnızca 100 baytlık bir dizi olarak bildirildiğini bilmenin bir yolu olmadığını unutmayın - yalnızca name'un başlangıcına işaretçiyi aldı - rec1.name'un bir sonlandırma \0 bayt olduğunu garanti etmek sizin sorumluluğunuzdaydı ve asla yaptı.

Biz biraz daha söyleyebilir. ("%f" biçimde oldukça çirkin) sayısı -9.2559631349317831e61rec1.balance değeridir. (Senin Intel ve tüm modern bilgisayarlar gibi) IEEE 754 bilgisayarında bu double değeri 8 bayt onaltılık 0xcccccccccccccccc içindedir. tuhaf sembolün Altmış dört sizin "%s" çıkış rec1.name sonunu kapalı tükendi böylece sadece 100-46 = 54 karakter 100 kalırken, rec1.name tekabül "%s" çıkışında görünecek ve pazarlık içine rec1.balance içermektedir ve Terminal programınızın ASCII olmayan 0xcc karakterini olarak yorumladığını öğrenin. 127'den büyük (0x7f) baytları yorumlamanın birçok yolu vardır; latin-1'de örneğin &Igrave; olurdu. Grafiksel karakter , eski MS-DOS karakter kümesinde, Windows kod sayfası 437'deki 0xcc (204) baytının gösterimidir. Yalnızca bir Intel makinesinde çalışmamanız değil, bir Windows makinesi (tabii ki büyük olasılıkla) ile başlamak için).

İlk iki sorunuzu yanıtlıyor. Üçüncü sorunuzu anladığımdan emin değilim. Umarım "dezavantajları" açıktır.

Nasıl düzeltileceğine gelince, fread kullanarak bir metin dosyasını okumak ve yorumlamak için makul bir yol yoktur. Bunu yapmak için, libcfscanf işlevindeki kodun çoğunu çoğaltmanız gerekir. Tek mantıklı yol, ilk kez bir ikili dosya oluşturmak için fwrite kullanmasıdır; Daha sonra fread, geri okumak için doğal olarak çalışacaktır. Yani iki program olmalı - biri ikili bir clients.bin dosyası yazacak ve bir saniye geri okuyacak. Tabii ki, bu ilk programın verilerinin ilk etapta nereden gelmesi gerektiği sorununu çözmez. fscanf kullanarak clients.txt okunabilir.

struct rec recs[] = {{100, "Jones", 564.90}, 
        {200, "Rita", 54.23}, 
        {300, "Richard", -45.00}}; 

Yoksa MySQL veritabanı okuma gelebilir ya ... tek bir yerde onu: Ya böyle struct rec bir dizi başlatarak örneğin fwrite programının kaynak kodu, dahil edilebilir Olası bir ikili dosyada (kolaylıkla) fread ile okunabilir olması muhtemel değildir.

+0

Vay, her parçayı çok kapsamlı bir şekilde açıklamak için zaman ayırdığınız için teşekkür ederim! – user153882