我在Java中使用非常大的.txt蛋白质文件数据库。蛋白质具有一般结构,但不是一个足够均匀的硬编码“从startIndex到endIndex,反向和替换”。唯一真正的一致性是它们由>
分隔,例如:
...WERINWETI>gi|230498 [Bovine Albumin]ADFIJWOENAONFOAIDNFKLSADNFATHISDATFDAIFJ>sp|234235 (human) AGP1 QWIQWONOQWNROIWQRNOQWIRNSWELLE>gi|...
等等。
正如你所看到的,虽然实际的蛋白质序列(所有大写的长链)是均匀的,因为它们是大写的链,但除此之外,前面的描述几乎可以是任何东西(有很多次不是描述和序列之间的空格)。我的程序需要做的是将原始文本复制到新文件,然后通过,在每个r-
之后添加>
(例如...EERFDS>r-gi|23423...
)以及仅反转链资本。完成该过程后,我需要将其附加到原始文本的末尾。
我已经完成了r-
功能,实际上我已经完成了逆转和追加,但效率不高。接受这种治疗的数据库是大量的,我的程序需要太长时间。事实上,我不知道需要多长时间,因为我永远不会让它完成。我等了1个小时才结束了。这是我使用正则表达式(内置模式类)(计算密集的部分)进行反转的算法:
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-
,但尚未经过逆转)转换成每个单独的蛋白质并将它们添加到一个String数组。然后我遍历数组中的每个字符串,查找大于5个字母的大写字母链,将匹配添加到StringBuffer,反转它,然后用反向替换正向版本。请注意,此算法适用于较小的文本文件。
是否有更强大的正则表达式可以消除分裂/遍历数组的需要?当我尝试时,replaceAll()
调用替换了所有下游蛋白质,与该组中的FIRST蛋白质相反。为了好玩,我用System.out.println(rDashMatcher.groupCount())
检查了它,并为该组中的每种蛋白质打印了0
。任何人都可以帮助我提高效率/强大的正则表达式吗?对我来说这是一个相当新的概念,但它让我想起了MATLAB中的矢量化(只有字母)。
答案 0 :(得分:2)
我在这里投入了10,000,000条记录(大约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();
}
答案 1 :(得分:1)
优化的一些想法:
最好与探查器一起运行,看看什么是消耗时间而不是猜测。例如,可以通过增加程序的内存或避免某些慢速文件系统等来提高性能。
答案 2 :(得分:1)
您不需要更强大的正则表达式,只需简化您的流程,这样您就不会一遍又一遍地处理相同的文本。在大多数情况下,这意味着使用Java的低级正则表达式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,因为这是API所要求的,但这并不是什么大问题;关于StringBuffer效率低下的报道,虽然如此,但往往被夸大了。
答案 3 :(得分:0)
正如我在评论中提到的,您不应该将整个文件加载到内存中。这将导致内存交换进入并使程序变慢。
如果“蛋白质”的大小,即>
分隔的字符串在内存中是可控的,这应该可以解决这个问题
Scanner scanner = null;
BufferedWriter writer = null;
try {
writer = new BufferedWriter(new FileWriter("output.txt"));
scanner = new Scanner(new BufferedReader(new FileReader("input.txt")));
scanner.useDelimiter(">");
while ( scanner.hasNext() ) {
doReverseAndWriteToFile(scanner.next(), writer);
}
} finally {
if ( scanner != null) {
scanner.close();
}
if ( writer != null ) {
writer.flush();
writer.close();
}
}
在doReverseAndWriteToFile()
你应该把你的程序的第二部分(我没注意:-))。在此功能中,您还应该随时写入新文件。
如果你使用它,你一次只能在内存中使用“bufferSize”+“一个蛋白长度”。
看看这是否会加快它...否则你必须寻找其他地方。