• 締切済み

C言語わかる方お願いしますm(_ _)m

大学の研究に必要なC言語を使ったプログラミングを作ることになったのですがプログラムを作ったことがないためわからないのでどなたかわかる方お願いします。 まずa.txtというfileがあるとしてそのファイルにはabcdの4文字がランダムに何千と続いています。 例えばacbbbaddddcbabbcddbbacbadabcdcbabddabcbdbcbadcabbadddbbcccbcdbabdbcbabcdcdbabcdaaadcadcdadcdacdbabcbbaacdacaddcdcabbbdcc・・・ といったファイルです。 これを8文字読んでは1文字ずらしてまた8文字読む・・・といった作業を繰り返していきます。上のファイルを例にすると最初のacbbbaddを読み次に1文字ずらしてcbbbadddを読むといった具合です。この8文字ずつとってきたものはabcdの4種類あるので8文字の種類としては4の8乗=65536通りあるのですがこの8文字がこのファイル中にそれぞれいくつあるかカウントしてくるプログラムを作りたいのですが自分にはわかりませんでした。今のところfopenしたあとfgetsで8文字ずつ読み込んだところまではいっています。 実行したときに aaaaaaaa 53 aaaaaaab 34 aaaaaaac 43   ・     ・    ・ といった感じに65536行の結果が得られるようにしたいのですが分かる方お願いいたしますm(_ _)m 本来情報系ではなく生化学の研究をしてるのですがこれのせいで先に進めない状態です。できるかたいましたらよろしくおねがいします。

みんなの回答

  • bravy
  • ベストアンサー率0% (0/2)
回答No.8

cat file | ./a.out とやると幸せになれるかも知れません #include <stdio.h> #include <stdlib.h> static unsigned long counter[65536]; static char* toabcd(int i) { static char abcd[9]; abcd[0]='a'+((i&0xc000)>>(2*7)); abcd[1]='a'+((i&0x3000)>>(2*6)); abcd[2]='a'+((i&0x0c00)>>(2*5)); abcd[3]='a'+((i&0x0300)>>(2*4)); abcd[4]='a'+((i&0x00c0)>>(2*3)); abcd[5]='a'+((i&0x0030)>>(2*2)); abcd[6]='a'+((i&0x000c)>>(2*1)); abcd[7]='a'+(i&0x0003); abcd[8]=0; return abcd;} int main() { long l; int i; int target=0; for(l=0;;l++) { int ch=getchar(); if(ch==EOF||ch=='\n') {break;} if('a'<=ch && ch<='d') { target=((target<<2|(ch-'a'))&0xffff); if(l<7) continue; counter[target]++;} else { fprintf(stderr,"invalid character at %ld = '%c'='%x'\n",l,ch,ch); exit(1);}} for(i=0;i<sizeof(counter)/sizeof(unsigned long);i++) { if(counter[i]!=0) {printf("%s %ld\n",toabcd(i),counter[i]);}}}

  • Oh-Orange
  • ベストアンサー率63% (854/1345)
回答No.7

★アドバイス ・まず何千と続いている a、b、c、d の文字データをすべてメモリ上に読み込みます。  ただし a=00h、b=01h、c=02h、d=03h として 2 ビットに変換します。  こうすると 8 文字の種類は 2 ビット× 8 文字=16ビットの塊として処理できます。  何千とあるデータですが最大10,000件あったとすると 1万件×2バイト=20,000バイト  のメモリですみます。つまり 20KB です。 ・そして 16 ビット(8文字分)を1つとした配列データを qsort などでソートします。  ソートされたデータを先頭から順番に重複データが幾つあるのかをカウントして  違うデータに移ったときに重複数をファイル(画面)に出力します。  これを1万件のデータが終わるまで繰り返します。 ・下にそのサンプルを載せておきます。 サンプル: #include <stdio.h> #include <stdlib.h> // 最大のデータ数 #define MAX_DATA (10000) // 関数のプロトタイプ宣言 int compDNA( const void *data1, const void *data2 ); int readDNA( FILE *fp, unsigned short DNA[], int size ); int read2Bit( FILE *fp ); void checkDNA( unsigned short DNA[], int size ); // メイン関数 int main( void ) {  unsigned short DNA[ MAX_DATA ] = { 0 };  FILE *fp;  int max;    // 読み込む  if ( (fp = fopen("DNA.txt","r")) != NULL ){   max = readDNA( fp, DNA, MAX_DATA );   fclose( fp );      // ソート   qsort( DNA, max, sizeof(unsigned short), compDNA );      // 重複チェック   checkDNA( DNA, max );  }  return 0; } // ソート用の比較関数 int compDNA( const void *data1, const void *data2 ) {  unsigned short comp1 = *(unsigned short *)data1;  unsigned short comp2 = *(unsigned short *)data2;    if ( comp1 < comp2 ) return -1;  if ( comp1 > comp2 ) return +1;  return 0; } // 読み込み用の関数 int readDNA( FILE *fp, unsigned short DNA[], int size ) {  unsigned short data = 0x0000;  unsigned short *pDNA = DNA;  int i, bit2;    // 最初だけ 7 文字読む(エラーチェックなし)  for ( i = 0 ; i < 7 ; i++ ){   data <<= 2;   data |= read2Bit( fp );  }  while ( (--size >= 0) && ((bit2 = read2Bit(fp)) != EOF) ){   data <<= 2;   data |= read2Bit( fp );   *pDNA++ = data;  }  return (int)(pDNA - DNA); } // 2ビットの読み込み int read2Bit( FILE *fp ) {  int ch;    if ( (ch = fgetc(fp)) != EOF ){   switch ( ch ){    case 'a': return 0x00;    case 'b': return 0x01;    case 'c': return 0x02;    case 'd': return 0x03;    default: return EOF;   }  }  return EOF; } // 重複チェック void checkDNA( unsigned short DNA[], int size ) {  unsigned short data;  int count = 1;  int i, j;    for ( i = 1 ; i < size ; i++ ){   if ( DNA[i - 1] == DNA[i] ){    count++;   }   else{    // 2ビットデータを復元表示    for ( data = DNA[i - 1], j = 0 ; j < 8 ; j++, data <<= 2 ){     putchar( "abcd"[ (data >> 14) & 0x03 ] );    }    printf( " %d\n", count );    count = 1;   }  } } 以上。

回答No.6

   これでできてるかな。 #include <stdio.h> #include <string.h> int p_to_d(char *p) { int d = 0, i; for(i = 16384; i > 0; i /= 4){ d += i * (*p ++ - 'a'); } return d; } int main(void) { FILE *fp = fopen("sample.txt", "r"); int c, i, j, k, count[65536] = {0,}; char p[9] = {'\0',}; if(!fp) return 1; fgets(p, 8, fp); if(strlen(p) < 7) return 1; while((c = fgetc(fp)) != EOF){ p[7] = c; count[p_to_d(p)] ++; memmove(p, &p[1], 7); } for(i = 0; i < 65536; i ++){ if(count[i]){ k = i; for(j = 7; j >= 0; j --){ p[j] = k % 4 + 'a'; k /= 4; } printf("%s %d\n", p, count[i]); } } fclose(fp); return 0; }  

回答No.5

8桁の文字列は 4進数8桁とみて10進数に直しましょうか 以下の文中で「数字に直す」とは  a → 0 b → 1 c → 2 d → 3 とします 1文字ずつ読みながら10進変換ができます x = 0 x = x + (1文字目を数字に直す) x = x * 4 x = x + (2文字目を数字に直す) x = x * 4 ...... x = x + (8文字目を数字に直す) /* 途中で4倍するのは 4進数の桁上がり */ これで、x には10進変換した値が計算されましたので y(x) = ++1 9文字目を読み、最初に戻る これで 最終的に y()に結果が格納されるとおもいますが いかがでしょうか? 追伸 C言語に自信がないので日本語表記です ごめんなさい

  • maku_x
  • ベストアンサー率44% (164/371)
回答No.4

計算対象ファイル(この場合は a.txt)が数10Kbytes程度までならば、perl で処理を書くのはいかがでしょうか。4~5年前以降の PC ならば、問題ない速度で処理できると思いますが。 (サンプルプログラム) #!/usr/bin/perl # open(FH, "a.txt"); while (<FH>) { chop; for ($ofs=0; $ofs<(length($_)-8); $ofs++) { $key = substr $_, $ofs, 8; if ($key !~ /[ \r]/) { $hash{$key}++; } } } foreach $key (sort(keys %hash)) { print "$key $hash{$key}\n"; }

  • alphion
  • ベストアンサー率19% (27/136)
回答No.3

>これを8文字読んでは1文字ずらしてまた8文字読む という部分が良くわかりませんが とりあえず、一文字ずつ読んで処理する方法で、細かい説明はしませんが、こんな感じでいかがでしょうか? #include<stdio.h> #include<stdlib.h> int m_countTbl[65536]; void initCountTbl() { int i; for(i=0;i<65536;i++) { m_countTbl[i]=0; } } void printCountTbl() { int i,j; for(i=0;i<65536;i++) { for(j=7;0<=j;j--) { printf("%c",'a'+((i>>(j*2))&3)); } printf(":%d\n",m_countTbl[i]); } printf("\n"); } int main(void) { int i; int ct; int val; int readVal; FILE *ifp; ifp=fopen("data.txt","r"); if(ifp!=NULL) { ct=0; val=0; initCountTbl(); while ( (readVal=fgetc(ifp)) != EOF) { val=(val<<2)&0xffff; if(readVal<'a' || readVal>'d') { if(8<=ct) { printCountTbl(); } ct=0; val=0; initCountTbl(); } else { val+=readVal-'a'; ct++; if(8<=ct) { m_countTbl[val]++; } } } fclose(ifp); } return 0; }

  • miya_0726
  • ベストアンサー率54% (94/173)
回答No.2

塩基配列の頻度解析ですね。 とりあえずfgetで8文字ずつ取ってくるのはあとの処理が面倒な気がするので、長い文字列をいったん全部配列に読み込んで、配列のインデックスをインクリメントしながら処理してみてはいかがですか? mRNAレベルならたかだか数十kbpですし、ゲノムレベルであっても最長の染色体で250Mbpですから、最近のPCであれば可能だと思います。 カウントは、65536種類のカウンタを準備するのは大変なので、65536個の配列をカウンタとして用意し、文字をカウンタのインデックスに対応付けるか、木構造のリストを作成するなどしてカウントしてみてはいかがでしょうか・・・?

回答No.1

別途65536個の配列を用意して、最初に全てを0で初期化しておき、パターンに応じてその番号の配列をインクリメントすればカウント出来ますね。