ara

2009-10-26 48 views
5

Aşağıdaki kod bir karakter anda bir metin dosyasını okur ve Stdout'a yazdırmak:ara

#include <stdio.h> 

int main() 
{ 
    char file_to_open[] = "text_file.txt", ch; 
    FILE *file_ptr; 

    if((file_ptr = fopen(file_to_open, "r")) != NULL) 
    { 
     while((ch = fgetc(file_ptr)) != EOF) 
     { 
      putchar(ch); 
     } 
    } 
    else 
    { 
     printf("Could not open %s\n", file_to_open); 
     return 1; 
    } 
    return(0); 
} 

Ama bunun yerine baskı stdout'a [putchar (ch)] İstiyorum Başka bir metin dosyasında sağlanan belirli dizeler için dosyayı arayın örn. strings.txt ve çıkış maçı ile hat

text_file.txt out.txt için:

 
1993 - 1999 Pentium 
1997 - 1999 Pentium II 
1999 - 2003 Pentium III 
1998 - 2009 Xeon 
2006 - 2009 Intel Core 2 

strings.txt: text_file.txt üç birinci satır eşleşir Bu durumda

 
Nehalem 
AMD Athlon 
Pentium 

. Ben C dosya işlemleri ile ilgili bazı araştırmalar yaptım ve o zaman ben fgetc [benim kodunda yaptığım gibi], fgets ile bir satır ve fread ile bir satır ile bir karakter okuyabilir, ama sanırım hiçbir kelime gibi görünüyor Benim durumumda mükemmel olur mu?

+3

neden bu programı yazıyoruz ?! Bunu yapmak için grep/awk/sed kullanın. –

+0

Hayır, Tim. Etiketler aramak içindir. Kimse bunu araştırmayacak. – GManNickG

+1

Evet, saniyeler içinde çözebileceğim standart Unix araçları ile biliyorum, ancak bu C dosyası IO'nun daha iyi anlaşılmasını sağlamaktır. –

cevap

7

Bu bir öğrenme alıştırması olduğu varsayılıyor ve sadece başlamak için bir yer arıyoruz. Aksi halde, tekerleği yeniden icat etmemelisiniz.

Aşağıdaki kod, neyin dahil olduğuna dair bir fikir vermelidir. Aranacak dosyanın adını ve bu dosyada arama yapmak için tek bir argüman belirtmenize izin veren bir programdır. İfadeleri bir dizi dizide aramaya koymak ve bu dizideki kelimelerin herhangi birinin okunan satırların herhangi birinde görünüp görünmediğini kontrol etmek için bunu değiştirebilmeniz gerekir.

Aradığınız anahtar işlevi strstr.

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

#ifdef DEBUG 
#define INITIAL_ALLOC 2 
#else 
#define INITIAL_ALLOC 512 
#endif 

char * 
read_line(FILE *fin) { 
    char *buffer; 
    char *tmp; 
    int read_chars = 0; 
    int bufsize = INITIAL_ALLOC; 
    char *line = malloc(bufsize); 

    if (!line) { 
     return NULL; 
    } 

    buffer = line; 

    while (fgets(buffer, bufsize - read_chars, fin)) { 
     read_chars = strlen(line); 

     if (line[read_chars - 1] == '\n') { 
      line[read_chars - 1] = '\0'; 
      return line; 
     } 

     else { 
      bufsize = 2 * bufsize; 
      tmp = realloc(line, bufsize); 
      if (tmp) { 
       line = tmp; 
       buffer = line + read_chars; 
      } 
      else { 
       free(line); 
       return NULL; 
      } 
     } 
    } 
    return NULL; 
} 

int 
main(int argc, char *argv[]) { 
    FILE *fin; 
    char *line; 

    if (argc != 3) { 
     return EXIT_FAILURE; 
    } 

    fin = fopen(argv[1], "r"); 

    if (fin) { 
     while (line = read_line(fin)) { 
      if (strstr(line, argv[2])){ 
       fprintf(stdout, "%s\n", line); 
      } 
      free(line); 
     } 
    } 

    fclose(fin); 
    return 0; 
} 

Örnek çıktı:

 
E:\Temp> searcher.exe searcher.c char 
char * 
    char *buffer; 
    char *tmp; 
    int read_chars = 0; 
    char *line = malloc(bufsize); 
    while (fgets(buffer, bufsize - read_chars, fin)) { 
     read_chars = strlen(line); 
     if (line[read_chars - 1] == '\n') { 
      line[read_chars - 1] = '\0'; 
       buffer = line + read_chars; 
main(int argc, char *argv[]) { 
    char *line; 
+0

Bu çok ilginç görünüyor. Doğru bir şekilde varsayıyorsunuz, bu benim için bir öğrenme alıştırmasıdır ve kaynağın daha önce çalışmış olduğum öğelerden oluştuğunu görebiliyorum, bu yüzden bu kodu tam olarak anlayabilmem gerekir. –

+0

C koduna oldukça yeniyim, ancak fds işlevinin '\ n' karakterinde durduğu için, salt read_line işlev çağrısını fgets işlev çağrısı ile değiştirip ana işlevdeki char * satırını rasgele büyük bir sayıya ayırdım. Read_line işlevinin amaçlanan amacını açıklayabilir misiniz? Orada çok fazla gereksiz kod var gibi görünüyor. – anon58192932

+1

@advocate [Ne kadar büyük büyüklükte?] (Http://en.wikipedia.org/wiki/Buffer_overflow) Makul boyutta bir arabellekle başladım ve gerektiğinde genişletmeye devam ediyorum. Aslında, herhangi bir satır sonu olmayan bir akış besliyorsa, bilgisayarınızın belleğinden çıkmasını engellemek için arabelleğin çok büyük bir hal alması gerektiğine dair başka bir kontrol olmalıdır, ancak bu basit bir öğrenme alıştırmasıydı. –

4

Unutmayın: fgetc(), getc(), getchar() hepsi bir tam sayı değil, bir karakter döndürür. Tamsayı EOF veya geçerli bir karakter olabilir - ancak char tipi tarafından desteklenen aralıktan bir değer daha verir.

Sen 'fgrep' komutu için, suret yazıyoruz: -() fgets kullanarak

fgrep -f strings.txt text_file.txt > out.txt 

yerine okuma karakterleri

, sen satırları okumak ihtiyacımız olacak. (Gets() işlevinin var olduğunu unutun!)

Kodunuzu girintiye ekledim ve bir geri dönüş 0 ekledim; Sizin için sonunda (C99 ana() 'nin sonuna düştüğünüzde' 0 'iadesi yapar. Bununla birlikte, C99 ayrıca her işlev için açık bir dönüş türü talep eder - ve siz 'int' öğesini sizin için 'int main()' olarak ekledim (ancak sonunda 0 döndürmediği için C99 uyumlu bahaneyi kullanamazsınız). Hata mesajları standart çıktıdan ziyade standart hataya yazılmalıdır.

Büyük olasılıkla dizeler listesi için dinamik ayırma kullanmanız gerekecektir. Basit fikirli bir arama basitçe 'strstr()' ifadesini, her bir giriş satırında gerekli olan dizelerin her birini ararken uygulayacaktır (bir eşleşme bulduğunuzda döngüyü kırdığınızdan emin olun, böylece birden fazla eşleşme varsa bir satır tekrarlanmayacaktır. tek bir satırda).

Daha karmaşık bir arama, hangi karakterlerin yok sayılabileceğini önceden hesaplar, böylece tüm dizeleri paralel olarak arayabilir, metin içinde döngü içinde döngüden daha hızlı atlayabilirsiniz. Bu, Boyer-Moore veya Knuth-Morris-Pratt (eklenmiş: veya çoklu dizeler için paralel arama yapmak üzere tasarlanmış olan Rabin-Karp) gibi bir arama algoritmasının modifikasyonu olabilir.

+0

şahsen ben karakterleri tamponlamak için bir işlev yazmayı tercih ediyorum ... sadece fgets kullanarak satır uzunluğu üzerinde keyfi sınırlar verir. – asveikau

+0

@asveikau: Farkı görmüyorum? Tamponları kullanırken tamponu sağlıyoruz, istediğimiz boyuta ayarlayabiliriz. Ve eğer strings.txt dosyasındaki satırlar tampondan daha uzunsa, yine de sorunumuz var demektir ... Yani, arabellek taşması durumunda bile arabellek taşması durumunu yönetmeliyiz? evet gerçekten ve bu, bir türlenmemiş arabellekten daha az belirgin. – kriss

+0

fgets(), belirtilen arabellek uzunluğuna kadar okur; Eğer alan bittiği zaman yeni bir satır ile karşılaşmadıysa, durur ve geri döner. Yani, son karakter yeni satır ve arabellek doluysa, daha fazla boşluk (reallocate?) Ek karakterleri koymak için, daha sonra fgets() 'i tekrar çağırıp (dikkatlice - bittiği yerden başlayarak) fazladan boşluk) ve daha fazlasını elde edin. Yani evet, veriyi dinamik olarak ayrılmış bir arabelleğe alabilmek için kendi okuyucunuzu yazabilir veya arabelleği taşırken okuma yapmak için fgets() kullanabilirsiniz. –

2

Bloklarla okuma her zaman daha iyidir, çünkü temel dosya sistemi nasıl çalışır?

Bu nedenle, yalnızca bloklara göre okuyun, sözcüklerinizden herhangi birinin arabellekte görünüp görünmediğini kontrol edin, ardından başka bir arabelleği okuyun. Arama sözcükleri tampon sınırındaysa, eksik olan algılamayı önlemek için yeni arabelleğin son birkaç karakterini yeniden yazmak için dikkatli olmanız gerekir.

bu önemsiz bir algoritma (sizin durumunuzda büyük olasılıkla gerçek değildir) çok daha sofistike bir algoritma bir tampon cf Rabin-Karp aynı anda birkaç alt dizeleri aramak için orada yeterli değilse.

+0

fgetc() kullandığınızda, stdio'nun bloklar ve arabellek karakterleri ile okuyacağından oldukça eminim ... – asveikau

+0

true, ama fgetc'i çağırmak, kendi başına bir maliyete sahip olup, girişi bir dizeyle (veya birkaç dizeyle) karşılaştırmak istiyorsanız bir yere kopyalaman gerekecek. Bu tam bir arabellek okuma ve onunla çalışmaktan çok daha büyük bir maliyeti var. Jonathan'ın önerdiği gibi tam bir satır okumak, tamponları doğrudan okumak için kendinize gory ayrıntılarını yönetmek istemiyorsanız, tam bir arabelleği okumak için iyi bir alternatiftir. – kriss

2
cat strings.txt |while read x; do grep "$x" text_file.txt; done 
+1

Yani fgrep -f strings.txt text_file.txt> out.txt'? –

+0

Evet, evet, fgrep -f dizeleri.txt text_file.txt'. Sanırım daha fazla pozlama daha fazla seçenek demektir. –

+0

Teşekkürler. Bunu yapmak için bir C programı yazmak, tam bir zaman kaybıdır. –