2015-01-15 5 views
1

Я написал код C (но не C pro), который должен быть как можно быстрее. Алгоритм закончен, и я доволен его скоростью. Но прежде чем он начнется, я должен получить некоторую информацию из текстового файла, что является способом замедления.Быстрая обработка текстовых файлов в C

В настоящее время обработка текстового файла требует около 3 секунд для больших файлов, в то время как один и тот же файл обрабатывается кодом Java менее чем за 1 секунду, потому что в Java есть готовые методы, такие как readline() в фреймворке, который содержит более 100 строк чистого кода.

Есть ли сопоставимые рамки для C? Я не мог найти что-либо в Google, потому что независимо от того, как я перефразировал свои поисковые запросы, я ничего не получил бы, но учебные пособия о том, как пользователю fopen() ...

Если вам интересно, почему я не использую Java, тогда : Сам алгоритм работает быстрее в C.

Вот код, который я использую в C. Что нужно сделать, это обработать файл .cnf в формате DINMACS.

while ((temp = fgetc(fp)) != EOF) 
    { 
     if (temp == 'c') 
     { 
      //evtl. im Labor auf 13 ändern 
      while ((temp =fgetc(fp)) != 10 && temp != EOF); 
     } 

     if (temp == 'p') 
     { 
      while ((temp =fgetc(fp)) < '0' || temp > '9'); 

      while (temp != 32) 
      { 
       variablen= (variablen * 10) + (temp - '0'); 
       temp=fgetc(fp); 

      } 

      while ((temp =fgetc(fp)) < '0' || temp > '9'); 

      while ((temp!= 32) && (temp != 10)) 
      { 
       klauseln= (klauseln * 10) + (temp - '0'); 
       temp=fgetc(fp); 
      } 

      while ((temp != 10) && (temp != EOF)) 
      { 
       temp=fgetc(fp); 
      } 

      break; 
     } 
    } 

    phi = (int *) malloc(klauseln * variablen * sizeof(int)); 

    int zaehler2 = 0; 
    for (int j = 0; j < klauseln; ++j) 
    { 
     for (int i = 0; i < variablen; ++i) 
     { 
      phi[zaehler2++] = 0; 
     } 
    } 

    int zeile = 0; 

    while ((temp = fgetc(fp)) != EOF) 
    { 
     if (temp == 'c') 
     { 
      while ((temp =fgetc(fp)) != 10 && temp != EOF); 
     } 
     else 
     { 
      while (temp != '0') 
      {       
        int neg = 1; 
        int wert = 0; 

        while (temp != 32) 
        { 
         if (temp == '-') 
         { 
          neg = -1; 
         } 
         else 
         { 
          wert = (wert * 10) + (temp - '0'); 
         } 

         temp = fgetc(fp); 
        } 
        phi[wert - 1 + zeile] = neg; 
        temp = fgetc(fp);  
      } 

      zeile = zeile + variablen; 
      temp = fgetc(fp);  
     } 
    } 
+3

Вы написали очень медленный код для обработки входного файла, где это? _ Если вы задаетесь вопросом, почему я не использую Java_: файл ввода-вывода также должен быть быстрее, но вы должны ошибаться, разместите код. –

+0

Когда вы помещаете вопрос как '[java]', это обычно означает, что вы хотите получить ответ на Java. Я бы не предполагал, что C быстрее, чем Java, если он был оптимизирован. BTW readLine() не предназначен для быстрой, но простой в использовании. Существует гораздо более быстрый способ чтения данных на Java. –

+0

Эй! Я редактировал код ниже. Я должен добавить, что я не продвинутый в C, Java или программировании вообще. Это первый раз, когда я обрабатываю любой текстовый файл в C. – joniboni

ответ

0

Я предполагаю, что вы ищете основные функции чтения файла и чтения символов один на один не направление вы ищете.

Существует множество функций для чтения и обработки строки в c. В stdio.h некоторые функции, которые могут помочь вам:

  • char * fgets (char * str, int num, FILE * stream): читать символы до конца строки или конца файла, если num достаточно велик.
  • int sscanf (const char * s, const char * format, ...);: считывает форматированный вход. Например, fscanf(line,"%d",&nb); прочитает целое число и поместит его в nb. Невозможно вызвать sscanf много раз в той же строке. Но байпас должен использовать strtok() из string.h, чтобы разделить строку, используя пробел " " в качестве разделителя.

Вот пример кода делает работу:

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


#define MAX_LINE_SIZE 1000 
#define MAX_SEQ_SIZE 100 

int main() 
{ 
    FILE * pFile; 
    char line [MAX_LINE_SIZE]; 
    int nbvar,nbclauses; 
    int* array=NULL; 
    int i=0;int j; 
    pFile = fopen ("example.txt" , "r"); 
    if (pFile == NULL){ perror ("Error opening file");} 
    else { 
     while (fgets(line, MAX_LINE_SIZE, pFile) != NULL){ 
      printf("%s",line);fflush(stdout); 
      // parsing the line 
      if(line[0]!='c' && line[0]!='\0'){ 
       if(line[0]=='p'){ 
        sscanf(line,"%*s%*s%d%d",&nbvar,&nbclauses); 
        array=malloc(MAX_SEQ_SIZE*nbclauses*sizeof(int)); 
       }else{ 
        char * temp; 
        char stop=0; 
        j=0; 
        //strtok split the line into token 
        temp=strtok(line," "); 
        while(stop==0){ 
         sscanf(temp,"%d",&array[i*(MAX_SEQ_SIZE)+j]); 
         temp=strtok(NULL," "); 
         if(array[i*MAX_SEQ_SIZE+j]==0){stop=1;} 
         j++; 
         printf("j %d\n",j);fflush(stdout); 
        } 
        i++; 
       } 

      } 
     } 
     fclose (pFile); 
    } 
    if(array!=NULL){ 
     for(i=0;i<nbclauses;i++){ 
      j=0; 
      while(array[i*MAX_SEQ_SIZE+j]!=0){ 
       printf("line %d seq item %d worths %d\n",i,j,array[i*MAX_SEQ_SIZE+j]); 
       j++; 
      } 
     } 
     free(array); 
    } 
    return 0; 
} 
+0

Эй, это мне очень помогло! Спасибо. – joniboni

+0

«Невозможно вызвать sscanf много раз в одной строке» Hmmm Много способов сделать 'while (stop == 0) {sscanf ... 'цикл с повторяющимися вызовами' sscanf() 'вызывает' temp/line'. – chux

3

Чтобы ускорить работу кода, вы сначала проверяете, есть ли лучший алгоритм.

Нет ничего алгоритмически неправильного. Вы обрабатываете каждый символ последовательно, без обратного отслеживания, так что это O (n), что так хорошо, как вы могли ожидать.

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

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

Может помочь сокращение количества проверок (сравнений). Например, когда вы проверяете 10 (linefeed) или EOF, вам нужно выполнить два теста для каждого символа. Если вы сначала прочитали файл в памяти, вы можете добавить дозор 10 в конец буфера, и тогда этот цикл должен будет проверять только на линии.

+2

что-то «алгоритмически неправильно» 'while ((temp = fgetc (fp)) <'0' || temp> '9');' - потенциальный бесконечный цикл. Когда происходит 'EOF', цикл никогда не заканчивается. – chux

+0

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

3

Я проверил тест, который читает символы из файла, используя fgetc(), другой - getc() (метод «e8») и буферизованная версия, которая собирает символы из локального буфера.

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

#define BUFLEN 1024 

FILE *fp; 
char fname[] = "test.txt"; 
int bufsize, bufind; 

int getachar() { 
    static unsigned char buf[BUFLEN]; 
    if (bufind >= bufsize) { 
     bufsize = fread(buf, sizeof(char), BUFLEN, fp); 
     if (bufsize == 0) 
      return -1; 
     bufind = 0; 
    } 
    return buf[bufind++]; 
} 

void WVmethod (void) { 
    int temp, count=0; 
    bufsize = bufind = 0; 
    if ((fp = fopen(fname, "rt")) == NULL) 
     return; 
    while ((temp = getachar()) != -1) count++; 
    fclose(fp); 
    printf ("WV method read %d chars. ", count); 
} 

void OPmethod (void) { 
    int temp, count=0; 
    if ((fp = fopen(fname, "rt")) == NULL) 
     return; 
    while ((temp = fgetc(fp)) != EOF) count++; 
    fclose(fp); 
    printf ("OP method read %d chars. ", count); 
} 

void e8method (void) { 
    int temp, count=0; 
    if ((fp = fopen(fname, "rt")) == NULL) 
     return; 
    while ((temp = getc(fp)) != EOF) count++; 
    fclose(fp); 
    printf ("e8 method read %d chars. ", count); 
} 

int main() 
{ 
    clock_t start, elapsed; 
    int loop; 

    for (loop=0; loop<3; loop++) { 
     start = clock(); 
     WVmethod(); 
     elapsed = clock() - start; 
     printf ("Clock ticks = %d\n", (int)elapsed); 

     start = clock(); 
     OPmethod(); 
     elapsed = clock() - start; 
     printf ("Clock ticks = %d\n", (int)elapsed); 

     start = clock(); 
     e8method(); 
     elapsed = clock() - start; 
     printf ("Clock ticks = %d\n", (int)elapsed); 

     printf ("\n"); 
    } 
    return 0; 
} 

выход программы:

WV method read 24494400 chars. Clock ticks = 265 
OP method read 24494400 chars. Clock ticks = 1575 
e8 method read 24494400 chars. Clock ticks = 1544 

WV method read 24494400 chars. Clock ticks = 266 
OP method read 24494400 chars. Clock ticks = 1591 
e8 method read 24494400 chars. Clock ticks = 1544 

WV method read 24494400 chars. Clock ticks = 265 
OP method read 24494400 chars. Clock ticks = 1607 
e8 method read 24494400 chars. Clock ticks = 1545 
+0

@chux улучшилось как ваше предложение. –

+0

Интересно! Интересно, почему 2-й WV-проход 374/249 медленнее первого прохождения WV? (Примечание: предположим 'bufsize = 0' сразу после' fopen() '). – chux

+0

@chux oops yes, не следует полагаться на инициализированные статические переменные, но он был мягким, поскольку «bufsize» начинается и заканчивается как 0. Временные значения всегда немного меняются, но в целом аналогичны показанным. На втором испытании WV должно быть важное задание Windows. –

Смежные вопросы