我在Java中使用了一个非常大的蛋白质.txt文件数据库。这些蛋白质有一个一般的结构,但没有统一到足以硬编码“将此从startIndex转移到endIndex,反转,并替换”的结构。唯一真正的一致性是它们由>分隔,例如:
...WERINWETI>gi|230498 [Bovine Albumin]ADFIJWOENAONFOAIDNFKLSADNFATHISDATFDAIFJ>sp|234235 (human) AGP1 QWIQWONOQWNROIWQRNOQWIRNSWELLE>gi|...等等。
正如你所看到的,虽然实际的蛋白质序列(所有大写字母的长链)是一致的,因为它们是大写字母链,但除此之外,前面的描述几乎可以是任何东西(在很多时候,描述和序列之间没有空格)。我的程序需要做的是将原始文本复制到一个新文件中,然后遍历,在每个>后面添加一个r- (例如...EERFDS>r-gi|23423...),并且只颠倒大写字母链。在该过程完成后,我需要将其附加到原始文本的末尾。
我已经完成了r-函数,实际上我也完成了反转和追加,但效率不够高。接受这种治疗的数据库是巨大的,而我的程序花费的时间太长。事实上,我不知道这需要多长时间,因为我从来没有让它结束。我等了一个小时,然后结束了。下面是我使用regex (内置Pattern类)(计算密集型部分)进行反转的算法:
Pattern regexSplit = Pattern.compile(">");
String[] splits = regexSplit.split(rDash.toString());
StringBuilder rDashEdited = new StringBuilder();
Pattern regexProtein = Pattern.compile("[A-Z]{5,}");
for (int splitIndex = 1; splitIndex < splits.length; splitIndex++) {
Matcher rDashMatcher = regexProtein.matcher(splits[splitIndex]);
rDashMatcher.find();
StringBuffer reverser = new StringBuffer(rDashMatcher.group());
rDashEdited.append(rDashMatcher.replaceAll(reverser.reverse().toString()) + ">");
}
System.out.println(">" + rDashEdited);因此,基本上我将rDash (它是一个StringBuilder,它包含所有放入>r-的原始蛋白质,但还没有经过反转)分割成每个单独的蛋白质,并将它们添加到字符串数组中。然后,我遍历数组中的每个字符串,查找长度超过5个字母的大写字母链,将匹配项添加到StringBuffer,反转它,并将正向版本替换为反转版本。请注意,此算法适用于较小的文本文件。
是否有更强大的正则表达式可以消除拆分/遍历数组的需要?当我尝试时,replaceAll()调用用集合中第一个蛋白质的反向替换了所有下游蛋白质。为了好玩,我用System.out.println(rDashMatcher.groupCount())查看了一下,它打印出了集合中每个蛋白质的0。有没有人能帮我做一个更高效/更强大的正则表达式?这对我来说是一个相当新的概念,但它让我想起了MATLAB中的矢量化(只有字母)。
发布于 2012-06-30 08:22:06
我花了1000万条记录(大约379MB的文本文件),花了1:06分钟。(4core athlon,几年前)
大的if树处理你只能得到一半的结尾处,因为分隔符在元素的中间。
public void readProteins(BufferedReader br, BufferedWriter bw) throws IOException
{
Pattern regexSplit = Pattern.compile(">");
Pattern proteinPattern = Pattern.compile("(.*?)([A-Z]{5,})");
Matcher m;
Scanner s = new Scanner(br);
s.useDelimiter(regexSplit);
while (s.hasNext())
{
StringBuffer sb = new StringBuffer();
String protein = s.next();
m = proteinPattern.matcher(protein);
if (m.find())
sb.append(m.group(2)).reverse().append(">r-").insert(0, m.group(1));
else
sb.append(protein);
);
}
bw.flush();
bw.close();
}发布于 2012-06-30 05:37:34
一些优化的想法:
最好是使用profiler来运行,看看是什么在消耗时间,而不是猜测。例如,可以通过增加程序的内存或避免某些缓慢的文件系统等来提高性能。
发布于 2012-06-30 13:29:42
你不需要一个更强大的正则表达式,你只需要简化你的过程,这样你就不会一遍又一遍地处理相同的文本。在大多数情况下,这意味着要使用Java的低级regex API,即appendReplacement()和appendTail()。通过向appendReplacement()传递一个空字符串,我避免了它对反向引用的自动处理。
也请注意我是如何使用find()的。如果您曾经发现自己调用find() (或者matches()或lookingAt())而没有检查它的返回值,那么您做错了什么。这样你就可以知道匹配是否成功了。
public static void main(String[] args) throws Exception
{
// this I/O code is bare-bones so as not to distract from the fun stuff
BufferedWriter bw = new BufferedWriter(new FileWriter("test_out.txt"));
// I use a lookahead so the ">" doesn't get discarded
Scanner sc = new Scanner(new File("test.txt")).useDelimiter("(?=>)");
while (sc.hasNext())
{
bw.write(reverseCapBlocks(sc.next()));
}
sc.close();
bw.close();
}
// cache these because recompiling them is fairly expensive
static final Pattern CAPS_PATTERN = Pattern.compile("\\b[A-Z]{5,}\\b");
static final Pattern BRACKET_PATTERN = Pattern.compile("^>");
static String reverseCapBlocks(String s)
{
StringBuffer sb = new StringBuffer();
Matcher m = CAPS_PATTERN.matcher(s);
while (m.find())
{
// appends whatever was between the last match and this one
// but hole off on appending the current match
m.appendReplacement(sb, "");
String temp = m.group();
// do the reversing manually because it's trivial and it avoids
// creating a new StringBuilder every time
for (int i = temp.length() - 1; i >= 0; i--)
{
sb.append(temp.charAt(i));
}
}
// append whatever was left after the last match
m.appendTail(sb);
// if the chunk began with ">", add the "r-"
return BRACKET_PATTERN.matcher(sb).replaceFirst(">r-");
}我使用StringBuffer而不是StringBuilder,因为这是应用程序接口需要的,但这没什么大不了的;有关StringBuffer效率低下的报道虽然是真的,但往往被高度夸大了。
https://stackoverflow.com/questions/11268946
复制相似问题