[英]unintended output character p when reversing a DNA string in C
预期的 output 是首先反转整个 DNA 字符串,然后转换 A <-> T,C <-> G。但是,在实际的 output 中,第一个出现的字符是“但是 output 字符串的 rest 很好。 这是代码:
int main() {
const char dna[] = "GATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTCTCCATGCAT"
"TTGGTATTTTCGTCTGGGGGGTGTGCACGCGATAGCATTGCGAGACGCTG"
"GAGCCGGAGCACCCTATGTCGCAGTATCTGTCTTTGATTCCTGCCTCATT"
"CTATTATTTATCGCACCTACGTTCAATATTACAGGCGAACATACCTACTA"
"AAGTGTGTTAATTAATTAATGCTTGTAGGACATAATAATAACAATTGAAT";
int dna_len = strlen(dna);
char rev_comp[dna_len+1];
char temp = '\0';
char temp_dna[dna_len+1];
for (int i = 0; i < dna_len + 1; i++) {
temp_dna[i] = dna[dna_len - i];
if (temp_dna[i] == 'A') {
temp = 'T';
rev_comp[i] = temp;
}
else if (temp_dna[i] == 'T') {
temp = 'A';
rev_comp[i] = temp;
}
else if (temp_dna[i] == 'C') {
temp = 'G';
rev_comp[i] = temp;
}
else if (temp_dna[i] == 'G') {
temp = 'C';
rev_comp[i] = temp;
}
}
rev_comp[dna_len+1] = '\0';
printf("original: %s\n", dna);
printf("rev_comp: %s\n", rev_comp);
return 0;
}
您的循环错误并循环到dna_len
( null 终止符所在的位置)。 它应该是:
for (int i = 0; i < dna_len; i++) { // corrected loop
temp_dna[i] = dna[dna_len - i - 1]; // corrected calculation
此外,rev_comp 中的最终 null 终止rev_comp
应分配在索引dna_len
,而不是dna_len + 1
- 这超出了范围,因此您的程序具有未定义的行为。 打印p
是未定义行为的一种可能结果。
rev_comp[dna_len] = '\0';
您创建了一个小助手 function 来在开始交换字符串中的字符之前进行反转。
void rev(const char *in, size_t len, char *out) {
for(size_t i = 0; i < len; ++i) {
out[len - i - 1] = in[i];
}
out[len] = '\0';
}
并调用它
rev(dna, dna_len, rev_comp);
在交换字母之前:
for (int i = 0; i < dna_len; i++) {
char *ch = &rev_comp[dna_len - i - 1];
switch(*ch) {
case 'A': *ch = 'T'; break;
case 'T': *ch = 'A'; break;
case 'G': *ch = 'C'; break;
case 'C': *ch = 'G'; break;
}
}
@TedLyngmo 已经指出了您原始代码中的索引错误,但您可能要考虑的另一个考虑因素是考虑能够重用您稍后在其他程序中编写的一些代码。 与其为您编写的每个单独程序一遍又一遍地编写专门的代码,不如识别您可能希望在另一个程序中再次使用的代码的公共部分,并为该部分代码创建一个简短的 function 使这成为可能。
您可能需要在此程序中多次反转字符串,因此编写一个可重用的 function 来反转您可以在任何需要的地方使用的字符串是有意义的。 根据您的职业道路,您可能还需要转换A <-> T
, C <-> G
,而不是在这个程序中,因此简短的 function 也可能有意义。
警告:如果需要最高效率(处理数十亿个字符串),那么结合这些操作并利用对 DNA 序列字符串的单次迭代是有意义的。 通过从字符串的每一端向中间工作,您可以在每次迭代中处理两个字符,从而将所需的迭代次数减少一半。
要为每个字符串的反转和转换制作可重用的 function,您可以编写如下函数。 字符串反转 function 显示了如何从每一端向中间工作,因为字符串有字符,所以只需要一半的迭代次数:
#include <stdio.h>
#include <string.h>
/* reverse src in dest copying 2-characters per-iteration. */
void strrev (char *dest, const char *src)
{
size_t begin = 0, end = strlen(src); /* begin and 1-past-end indexes */
dest[end--] = 0; /* nul-terminate dest */
if (end < 1) /* if less than 2 chars, return */
return;
do {
dest[begin] = src[end]; /* end to begin */
dest[end] = src[begin]; /* begin to end */
} while (--end > ++begin); /* increment & test */
}
/* transform A <-> T, C <-> G */
void xformATCG (char *s)
{
do {
if (*s == 'A')
*s = 'T';
else if (*s == 'T')
*s = 'A';
else if (*s == 'C')
*s = 'G';
else if (*s == 'G')
*s = 'C';
} while (*++s);
}
如果您愿意,您可以编写一个简单的打印 function ,它将以特定数量的字符打破 output 的长行,类似于您在初始化dna[]
时显示的方式。 对于它的价值,您可以添加:
/* simple print with break at brk chars function */
void prnwbrk (const char *s, size_t brk)
{
size_t n = 0; /* counter */
while (s[n]) { /* loop until end-of-string */
if (n && n % brk == 0) /* if brk chars, output \n */
putchar ('\n');
putchar (s[n++]); /* output char */
}
putchar ('\n'); /* final \n */
}
现在反转和转换字符串只需在main()
中调用strrev()
和xformATCG()
) 即可。 您可以在每个操作之间检查每个步骤(这使得调试更容易)。 一个简短的main()
可能是:
int main (void) {
const char dna[] = "GATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTCTCCATGCAT"
"TTGGTATTTTCGTCTGGGGGGTGTGCACGCGATAGCATTGCGAGACGCTG"
"GAGCCGGAGCACCCTATGTCGCAGTATCTGTCTTTGATTCCTGCCTCATT"
"CTATTATTTATCGCACCTACGTTCAATATTACAGGCGAACATACCTACTA"
"AAGTGTGTTAATTAATTAATGCTTGTAGGACATAATAATAACAATTGAAT";
char rev_comp[sizeof dna];
prnwbrk (dna, 50); /* print original dna */
putchar ('\n');
strrev (rev_comp, dna); /* reverse and print */
prnwbrk (rev_comp, 50);
putchar ('\n');
xformATCG (rev_comp); /* transform chars and print */
prnwbrk (rev_comp, 50);
}
示例使用/输出
如果我正确理解了您的问题和操作,则反转和转换后的字符串将如下所示:
$ ./bin/revdna
GATCACAGGTCTATCACCCTATTAACCACTCACGGGAGCTCTCCATGCAT
TTGGTATTTTCGTCTGGGGGGTGTGCACGCGATAGCATTGCGAGACGCTG
GAGCCGGAGCACCCTATGTCGCAGTATCTGTCTTTGATTCCTGCCTCATT
CTATTATTTATCGCACCTACGTTCAATATTACAGGCGAACATACCTACTA
AAGTGTGTTAATTAATTAATGCTTGTAGGACATAATAATAACAATTGAAT
TAAGTTAACAATAATAATACAGGATGTTCGTAATTAATTAATTGTGTGAA
ATCATCCATACAAGCGGACATTATAACTTGCATCCACGCTATTTATTATC
TTACTCCGTCCTTAGTTTCTGTCTATGACGCTGTATCCCACGAGGCCGAG
GTCGCAGAGCGTTACGATAGCGCACGTGTGGGGGGTCTGCTTTTATGGTT
TACGTACCTCTCGAGGGCACTCACCAATTATCCCACTATCTGGACACTAG
ATTCAATTGTTATTATTATGTCCTACAAGCATTAATTAATTAACACACTT
TAGTAGGTATGTTCGCCTGTAATATTGAACGTAGGTGCGATAAATAATAG
AATGAGGCAGGAATCAAAGACAGATACTGCGACATAGGGTGCTCCGGCTC
CAGCGTCTCGCAATGCTATCGCGTGCACACCCCCCAGACGAAAATACCAA
ATGCATGGAGAGCTCCCGTGAGTGGTTAATAGGGTGATAGACCTGTGATC
在main()
中做这一切并没有错,但提前思考可以避免每次你需要在另一个程序中做同样的事情时重新发明轮子。 (此外,编写和调试一次 function 可防止以后重新发明 function 时出现新错误)
如果您还有其他问题,请仔细查看并告诉我。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.