• 締切済み

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で初期化しておき、パターンに応じてその番号の配列をインクリメントすればカウント出来ますね。

関連するQ&A

  • C言語 乱数

    C言語 乱数 プログラミングの宿題なのですが、よく分かりません。教えていただける方、よろしくお願いします。 ・表示する文字数の長さは12とする。 ・表示する文字は毎回ランダムで表示すること。 ・文字は英字のうち、小文字のみとする。 ・プログラムにrandom()を使うこと。 ・プログラムにsrandom()を使うこと。 よろしくお願いします。

  • c言語について教えてほしい

    教えてください。 現在c言語を自習していますが、偶然にこういう問題を見ましたが、なかなか解けなくて、助けがほしいです。お願いします。 問題: fopen,fputs,fcloseを使って、次のプログラムを作成しなさい。文字列とファイル名を入力させ、ファイルを生成する。(入力したファイル名に拡張子.txtを付与する。) 表示イメージ: 文字列を入力して下さい。 ファイル書き込みテストをします。注意します (←入力する) ファイル名を入力して下さい。 testei (←入力する) ファイルの書き込みに成功しました。 ファイルイメージ: testi.txt (←入力したファイル名 拡張子.txtが付与される) ファイル書き込みテストをします。注意します (←入力した文字列)

  • C言語でセグメンテーションエラーの原因がわからず困っています。

    こんにちは。いつもお世話になっております。 C言語なのですが、何故かプログラムを実行するとセグメンテーション違反が表示されてしまい全く動きません。皆さんのお力を貸して頂けないでしょうか。 以下が問題のプログラムです。 前提条件: ・別プログラムによりfile.txtは末尾に文章が追加され続けている ・本プログラムは永劫的に動き続ける #include<stdio.h> #include<stdlib.h> #include<string.h> #include<unistd.h> #include<errno.h> #include<signal.h> #include<sys/types.h> int main () { int i = 1, a; FILE *fp, *fp2; char *tok; //NULLを入れて初期化 char buf[1024] = ""; char str[1024] = ""; char str2[1024] = ""; //出力するファイルを開く if ((fp2 = fopen ("out.txt", "a")) == NULL) { printf ("out.txt:open error\n"); exit (-1); } while (1) { //何度もfopenするのでループ二週目以降は一旦fpを閉じる if (str2 != NULL) { fclose (fp); } //一度読み込み、2週目以降もさらに読み込み続ける if ((fp = fopen ("file.txt", "r")) == NULL) { printf ("file.txt:open error\n"); exit (-1); } //str2はループ2週目以降で使われる if (str2 != NULL) { while (1) //新しい行を見つけ出す { fgets (str, 1024, fp); if (strstr (str, str2) != NULL) { printf ("前回の最終行です:%s\n", str); break; } } } else { printf ("str2はNULLです:%s\n", str2); } //fopenで読み込んだ現段階のテキストファイルをout.txtに移す while (1) { if( (fgets (str, 1024, fp) ) == NULL)break; if ((a = strlen (str)) >= 2) { str[a - 1] = '\0'; } else { printf ("抽出した文字列が1文字以下です:%s\n", str); break; } if (strlen (str) >= 17) { strcpy (str2, str); } else { printf ("抽出した文字列が16文字以下です:%s\n", str); break; } printf ("%s\n", buf); fprintf (fp2, "%s\n", buf); strcpy (buf, ""); strcpy (str2, str); } printf ("これが検索語です:%s\n", str2); } fclose (fp2); return 0; } プログラムの内容は、 更新され続けるテキストファイル(file.txt)から、別のテキストファイル(out.txt)に出力するというプログラムです。(file.txtの内容を若干変えつつ、out.txtに出力することが本来の目的ですが、その部分は省略させて頂きました。) fopenで開くと、「その時点まで」のfile.txtしか開かれません。更新され続けているfile.txtの内容をコンスタントに読み取る為に、while文でfopenし続ける方法を取っていますが、そうするとout.txtに移し終わった行まで読み込んでしまい重複した文章をout.txtに移すことになりますので、strstrを使い、「前回fopenで開いたfile.txt」の最終行を検索してその位置からまたout.txtに移す、という方法を取りました。しかし、結果は何故かセグメンテーション違反でした。 以上のプログラムや私の意図する所で何か気づかれた点や、おかしいと思われた点等ありましたら是非ご教授頂きたく思います。 どんな些細なことでも構いませんので、教えて頂けないでしょうか。 環境はCentosです。どうぞよろしくお願いします。

  • C言語

    C言語で簡単なプログラムを作っているのですが、 「テキストファイルが存在し、かつ空のテキストファイル(開いても何も書かれていない)だと、そのテキストファイルを削除する」 というコードはどうのように書けばいいのでしょうか? 現在自分が考えているのは、 #include<stdio.h> int main(){ FILE *fp; if((fp=fopen("example.txt","r"))==NULL){ printf("file not open\n"); }else if(((fp=fopen("example.txt","r"))!=NULL)&&(fgetc(fp)==NULL)){ //「ファイルがあって、中に何も書かれていない」という条件式を書きたい// remove("example.txt"); } return 0; } 大学で講義としてC言語を習っている程度の知識です。 fopenのif文の書き方があってるかどうかもわからないんですが^^; borland c++ 5.5.1を使用しています。 よろしくお願いします。

  • c言語についての質問です

    あるdatファイルに時間と振幅のデータが入っているんですが振幅のデータのみを2乗してから2乗したものに1/2乗し正のものだけから振幅の最大値を求め、振幅の最大値の2割に達した時間を出力したいのですが、どのようにすればよいか分かりません。 サンプルプログラムを載せますのでおねがいします。 ちなみにdatファイルには 時間 振幅 時間 振幅 … となっています。 #include <stdio.h> int main(void) { const char * fname = "C2.dat"; char line[BUFSIZ]; double amp, time,arrival, max=0,max20; FILE *fp = fopen(fname, "r"); while (fgets(line, BUFSIZ, fp)) { sscanf(line, "%lf%lf", &time, &amp); if (amp > max) { max = amp; } } fclose(fp); printf("max: %.7lf\n", max); fp =fopen(fname,"r"); while (fgets(line, BUFSIZ, fp)){ sscanf(line, "%lf%lf" , &time, &amp); if(amp > max*0.2){ arrival = time; break; } } fclose(fp); printf("arrival:%.16lf\n",arrival); return 0; }

  • C言語のプログラミングについて

    C言語のプログラミングについて プログラミング初心者です。下のプログラムに詰まりました。 下のプログラムをfgets()、atoi()の組とgetchar()を用いて表示するにはどのようなプログラムを書けばよろしいのでしょうか? % ./descending 正の整数: 10 英文字: z 1: z 2: y 3: x 4: w 5: v 6: u 7: t 8: s 9: r 10: q %

  • C言語でファイルを出力

    ファイルの中に変数をいれて複数のファイルをつくろうとしたのですが、 できたファイルの後に?マークがついてきます。 file = fopen("filename.txt", "w"); for(j=0; j<30; j++){ fprintf(file ,"%d.dat\n",j ); } fclose(file); file2 = fopen("filename.txt", "r"); として、ファイル名を書いたファイルをつくってから、 for(k=0; k<30; k++){ fgets(fp,sizeof(fp),file2); file_out = fopen(fp,"w"); 省略 fprintf(file_out, %e %e \n",a ,b); fclose(file_out); } fclose(file2); をして、30個のファイルを出力すると、 0.dat? 1.dat? 2.dat? . . . 29.dat? というファイルができてしまいます。 ファイルの中はしっかりできています。 なにか解決法を知っている方がおりましたら、どうか教えて下さい。

  • C言語でのファイルへの出力について

    1つのプログラムで、複数のファイルを出力したいと考えております。 ファイル名も test_1.txt test_2.txt   ・   ・   ・ test_100.txt というように、番号のみ違うものに。 そして手動ではなく、for文などを用いてファイル名を決めたいです。 つまり、 for(i=0;i<100;i++){   fp = fopen("test_i.txt","w") } みたいなことをしたいのです。 でも上記のようだとエラーが出ます。 どうしたらよろしいのでしょうか…。

  • C++で文字をカウントするプログラムを・・・

    C++で簡易的な文字をカウントするプログラムを作成しようとしています。 あるtxtファイルがあって、その中に ----------------- nannchara8 1234 #kokokara a92kv838 402853 #s82ffr kka0345 #kokomade ----------------- と記述してあります。 #kokokara~#kokomadeに書いてある特定の文字(0やaなど)の数をカウント、ただし、行頭に#のついているものはカウントしない(上に示したtxtファイル内の文字「8」のカウント結果は3・・・みたいな」)。 そんなプログラムを作りたいのですが どのようにすればいいでしょうか? ご教授願います。

  • C言語の使い方を教えてください。-C言語入門者

    C言語のプログラムの処理の対象となるファイルはどこに保存したらよいのでしょうか?たとえば下記のプログラムで #include <stdio.h> #include <stdlib.h> #include <string.h> void main(void) { char *line[100], *buf; int i = 0, len, lines; FILE *fp; fp = fopen("textfile.txt", "r"); buf = calloc(128, sizeof(char)); while (i < 100 && fgets(buf, 128, fp) != NULL) { len = strlen(buf); line[i] = calloc(len + 1, sizeof(char)); strcpy(line[i], buf); i++; } fclose(fp); lines = ((i <= 100) ? i :100); free(buf); for(i = 0; i < lines; i++) printf("%s", line[i]); for(i = 0; i < lines; i++) free(line[i]); } で [textfile.txt]という名のファイルをマイドキュメントやコンパイラーを保存しているのと同じ領域に保存してコンパイラーを実行しても所要の結果が得られません。テキストにはプログラムの作り方しか書いてありません。

専門家に質問してみよう