使用我创建的二进制文件中的fread()进行分段错误

时间:2018-05-10 03:47:13

标签: c

我是python的新手。我正在尝试编写两个c脚本,一个读取FASTA格式的纯文本文件(用于DNA / RNA /蛋白质序列)。他们看起来像这样......

>sequence1
ATCTATGTCGCTCGCTCGAGAGCTA
>sequence2
CGTCGCTGGGATCGATTTCGATAGCT
>sequence3
AAATATAACTCGCTAGCTCGATCGATC
>sequence4
CTCTCTCCTCTCTCTATATAGGGG

...个别序列由“>”分隔字符。在每个序列中,实际序列及其标签由换行符分隔。 (即“>标签\ n序列”)。用于读取纯文本然后将其写入二进制文件的脚本似乎可行。但是,当我尝试读取二进制文件并打印其内容时,我收到了分段错误(Core dump)。

我试图在这里发布一个简化的示例,但是这个例子似乎没有错误。所以,我觉得我不得不在这里附上我的整个代码片段。我一定错过了什么。

这是第一个读取纯文本fasta文件的脚本,首先将其拆分为“>”字符,然后由换行符,为上述FASTA文件中的每个序列创建“序列”结构。然后将这些结构写入“your_sequences.bin”。

#include <stdio.h>
#include <string.h>
#define BUZZ_SIZE 1024
struct sequence {
    char *sequence;
    char *label;
};
int main(int argc, char *argv[]) {

    FILE *fptr;
    char buffer[BUZZ_SIZE];
    char fasta[BUZZ_SIZE];
    char *token;
    char *seqs[3];
    int idx = 0;
    const char fasta_delim[2] = ">";
    const char newline[3] = "\n";

    /* Read-in plain-text */
    fptr = fopen(argv[1],"r");

     while (fgets(buffer, BUZZ_SIZE, fptr) != NULL) {
         strcat(fasta, buffer);
     }

     fclose(fptr);

     /* Process text, first by splitting by > and then by \n for each sequence, and then write to binary */

     FILE *out;
     out = fopen("your_sequences.bin","wb");
     struct sequence final_entry;

     token = strtok(fasta,fasta_delim);
     while (token != NULL) {
         seqs[idx++] = token; 
         token = strtok(NULL,fasta_delim);
        }

     for (idx=0; idx<4; idx++) {
        token = strtok(seqs[idx],newline);
        char *this_seq[1];
        int p = 0;
        while (token != NULL) {
            this_seq[p] = token;
            token = strtok(NULL,newline);
            p++;
        }
        final_entry.label = this_seq[0];
        final_entry.sequence = this_seq[1];
        printf("%s\n%s\n\n", final_entry.label, final_entry.sequence);
        fwrite(&final_entry, sizeof(struct sequence), 1, out);
     }
     fclose(out);

     return(0);
}

这正如预期的fprint()声明一样输出到底部:

sequence1
ATCTATGTCGCTCGCTCGAGAGCTA

sequence2
CGTCGCTGGGATCGATTTCGATAGCT

sequence3
AAATATAACTCGCTAGCTCGATCGATC

sequence4
CTCTCTCCTCTCTCTATATAGGGG

我认为错误必须在上面的脚本中(即我的二进制文件搞砸了),因为分段错误是由下面脚本中的fread()语句引起的。我不认为我在调用fread()时犯了错误,但也许我错了。

#include <stdio.h>
#define BUZZ_SIZE 1024

struct sequence {
    char *sequence;
    char *label; };

int main(int argc, char *argv[]) {

    struct sequence this_seq;
    int n;
    FILE *fasta_bin;

    fasta_bin = fopen(argv[1],"rb");
    for (n=0;n<4;n++) {
        fread(&this_seq, sizeof(struct sequence), 1, fasta_bin);
        printf (">%s\n%s\n", this_seq.label, this_seq.sequence);
    }

    fclose(fasta_bin);

    return(0); 

}

这会输出分段错误

[1]    8801 segmentation fault (core dumped)

在过去的几个小时里,我已经修好并且花了很多钱。我希望我没有犯下一些愚蠢的错误,浪费你的时间!

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

  

我认为错误必须在上面的脚本中(即我的   二进制文件搞砸了),

排序。

  

因为分段错误是由   下面脚本中的fread()语句。

我非常确信错误不会发生在fread()中,而是发生在以下printf()中。

  

我认为我没有做过   调用fread()时出错,但也许我错了。

您的fread()对应fwrite()。我们完全有理由期待您准确地回读所写的内容。这里的主要问题是C新手的一个常见问题:你误解了C字符串的性质(char的空终止数组),并且没有意识到关键,但是数组和指针之间的细微区别。

为了扩展这一点,C没有第一类字符串数据类型。相反,标准库提供“字符串”函数,这些函数对类型为char的对象序列进行操作,其中序列的末尾由终止符char标记,值为0.此类序列通常包含在char数组中,始终可以像对待它们一样对待。因为这是标准库支持的,所以该约定也普遍用于程序和第三方库中。

但是,

C没有将数组传递给函数或将它们作为返回值接收的机制。赋值运算符或大多数其他运算符也不适用于数组 - 甚至不是索引运算符[]。相反,在大多数情况下,数组类型的值会自动转换为指向第一个数组元素的指针,并且这些值可以传递给各种运算符并用作操作数。看到(部分)这个,缺乏经验的C程序员经常错误地用这样的指针识别字符串而不是指向数据。

当然,指针值只是一个地址。您可以复制它并将其存储在程序中的任意位置,但这对指向的数据没有任何作用。现在我终于明白了:你也可以写出一个指针值并将其读回来,就像程序那样,但这样做很少有用,因为指向的是当你这样做时,数据不会出现。除非您将指针读回到写入它的同一进程中,否则回读指针值不太可能有效,并且它与编写它的程序中的确无法达到相同的意义。

您必须改为编写指向数据,但必须选择一种格式。特别是,标题和序列通常具有不同的长度,您需要决定的关键事项之一是,如果有的话,您的二进制格式应该如何反映这一点。但是,如果我可能会如此大胆,我建议您使用一种定义明确的格式:Fasta格式!严重。

没有太多的数据压缩能够更加紧凑地表达fasta格式的数据,因为这种格式只需要表达它传达的变长数据所需的数据。那么,您需要回答的问题是,您正在尝试通过重新格式化来实现什么 - 根本就是重新格式化的原因,以及基于此目标格式实际上是什么。

答案 1 :(得分:1)

您正在获得分段错误,因为在您的程序中,您正在使用指针而不为它们分配内存:

        printf (">%s\n%s\n", this_seq.label, this_seq.sequence);

首先需要为this_seq.labelthis_seq.sequence指针分配内存,如下所示:

this_seq.sequence = malloc(size_of_sequence);
if (this_seq.sequence == NULL)
   exit(EXIT_FAILURE);

this_seq.label = malloc(size_of_label);
if (this_seq.label == NULL)
   exit(EXIT_FAILURE);

然后将数据读入它们,如下所示:

fread(this_seq.sequence, size_of_sequence, 1, fasta_bin);
fread(this_seq.label, size_of_label, 1, fasta_bin);