使用Java,我有一个类,它将网页检索为字节数组。然后我需要删除一些内容(如果存在)。 (该应用程序监视网页的变化,但需要从PHP创建的html中删除会话ID,并且意味着每次访问该页面时都会检测到更改。)
一些生成的字节数组可能是1000个字节长的10。它们不是这样存储的 - 存储了16字节的MD5页面。但是,它是需要处理的原始全尺寸字节数组。
(更新 - 代码不起作用。请参阅以下A.H.的评论) 测试显示我的代码:
public void testSessionIDGetsRemovedFromData() throws IOException
{
byte[] forumContent = "<li class=\"icon-logout\"><a href=\"./ucp.php?mode=logout&sid=3a4043284674572e35881e022c68fcd8\" title=\"Logout [ barry ]\" accesskey=\"x\">Logout [ barry ]</a></li>".getBytes();
byte[] sidPattern = "&sid=".getBytes();
int sidIndex = ArrayCleaner.getPatternIndex(forumContent, sidPattern);
assertEquals(54, sidIndex);
// start of cleaning code
ArrayList<Byte> forumContentList = new ArrayList<Byte>();
forumContentList.addAll(forumContent);
forumContentList.removeAll(Arrays.asList(sidPattern));
byte[] forumContentCleaned = new byte[forumContentList.size()];
for (int i = 0; i < forumContentCleaned.length; i++)
{
forumContentCleaned[i] = (byte)forumContentList.get(i);
}
//end of cleaning code
sidIndex = ArrayCleaner.getPatternIndex(forumContentCleaned, sidPattern);
assertEquals(-1, sidIndex);
}
这一切都很好,但我担心清洁部分的效率。我原本希望只在数组上运行,但ArrayList有很好的内置函数来从ArrayList中删除一个集合,这正是我所需要的。所以我不得不创建一个Byte的ArrayList,因为我不能拥有原始字节的ArrayList(任何人都可以告诉我为什么?),将模式转换为删除到另一个ArrayList(我想这可能是一个ArrayList一直)传递给removeAll()。然后我需要创建另一个byte []并将Bytes的ArrayList的每个元素转换为一个字节并将其添加到byte []。
有没有更有效的方法来做这一切? 可以使用数组执行吗?
的更新 的 这与使用字符串的功能相同:
public void testSessionIDGetsRemovedFromDataUsingStrings() throws IOException
{
String forumContent = "<li class=\"icon-logout\"><a href=\"./ucp.php?mode=logout&sid=3a4043284674572e35881e022c68fcd8\" title=\"Logout [ barry ]\" accesskey=\"x\">Logout [ barry ]</a></li>";
String sidPattern = "&sid=";
int sidIndex = forumContent.indexOf(sidPattern);
assertEquals(54, sidIndex);
forumContent = forumContent.replaceAll(sidPattern, "");
sidIndex = forumContent.indexOf(sidPattern);
assertEquals(-1, sidIndex);
}
这是否与array / arrayList方法一样有效?
谢谢, 百里
答案 0 :(得分:5)
您可以使用List#toArray()
将任何列表转换为数组。
在这个特定用例中,事情有点复杂,因为在转换列表时没有优雅的方式来自动解包(从Byte
到byte
)。好的'Java泛型。这是一个很好的细节...
所以我不得不创建一个Byte的ArrayList,因为我不能拥有原始字节的ArrayList(有人可以告诉我为什么吗?)
因为在Java中,泛型类型参数不能是原语。见Why can Java Collections not directly store Primitives types?
旁注:作为一种风格问题,您几乎应该始终将ArrayList
类型声明为List
:
List<Byte> forumContentList = new ArrayList<Byte>();
请参阅Java - declaring from Interface type instead of Class和Type List vs type ArrayList in Java。
答案 1 :(得分:3)
一切正常,我担心清洁部分的效率......
真的?你检查了结果“字符串”了吗?在我的机器上,forumContentCleaned
中的数据仍包含&sid=...
数据。
那是因为
forumContentList.removeAll(Arrays.asList(sidPattern));
尝试从List<byte[]>
中删除List<Byte>
。这无济于事。即使您将removeAll
的参数替换为包含List<Byte>
字节的真实"&sid="
,您也会删除每个a
的所有次出现},每个m
,每个p
等等。结果数据如下所示:
<l cl"con-logout">< href"./uc.h?oelogout34043284674572e35881e022c68fc8" ttle....
嗯,严格来说,&sid=
部分已经消失,但我很确定这不是你想要的。
因此,退一步思考:你在这里进行字符串操作,所以使用StringBuilder
,用String(forumContent)
提供它并在那里进行操作。
修改强>
查看给定的示例输入字符串,我想,还应删除sid
的值,而不仅仅是键。此代码应该在没有常规表达的情况下有效地执行:
String removeSecrets(String input){
StringBuilder sb = new StringBuilder(input);
String sidStart = "&sid=";
String sidEnd = "\"";
int posStart = 0;
while ((posStart = sb.indexOf(sidStart, posStart)) >= 0) {
int posEnd = sb.indexOf(sidEnd, posStart);
if (posEnd < 0) // delete as far as possible - YMMV
posEnd = sb.length();
sb.delete(posStart, posEnd);
}
return sb.toString();
}
修改2
以下是StringBuilder
和String.replaceAll
之间的一个小基准:
public class ReplaceAllBenchmark {
public static void main(String[] args) throws Throwable {
final int N = 1000000;
String input = "<li class=\"icon-logout\"><a href=\"./ucp.php?mode=logout&sid=3a4043284674572e35881e022c68fcd8\" title=\"Logout [ barry ]\" accesskey=\"x\">Logout [ barry ]</a>&sid=3a4043284674572e35881e022c68fcd8\"</li>";
stringBuilderBench(input, N);
regularExpressionBench(input, N);
}
static void stringBuilderBench(String input, final int N) throws Throwable{
for(int run=0; run<5; ++run){
long t1 = System.nanoTime();
for(int i=0; i<N; ++i)
removeSecrets(input);
long t2 = System.nanoTime();
System.out.println("sb: "+(t2-t1)+"ns, "+(t2-t1)/N+"ns/call");
Thread.sleep(1000);
}
}
static void regularExpressionBench(String input, final int N) throws Throwable{
for(int run=0; run<5; ++run){
long t1 = System.nanoTime();
for(int i=0; i<N; ++i)
removeSecrets2(input);
long t2 = System.nanoTime();
System.out.println("regexp: "+(t2-t1)+"ns, "+(t2-t1)/N+"ns/call");
Thread.sleep(1000);
}
}
static String removeSecrets2(String input){
return input.replaceAll("&sid=[^\"]*\"", "\"");
}
}
结果:
java version "1.6.0_20"
OpenJDK Runtime Environment (IcedTea6 1.9.9) (6b20-1.9.9-0ubuntu1~10.04.2)
OpenJDK 64-Bit Server VM (build 19.0-b09, mixed mode)
sb: 538735438ns, 538ns/call
sb: 457107726ns, 457ns/call
sb: 443282145ns, 443ns/call
sb: 453978805ns, 453ns/call
sb: 458895308ns, 458ns/call
regexp: 2404818405ns, 2404ns/call
regexp: 2196834572ns, 2196ns/call
regexp: 2239056178ns, 2239ns/call
regexp: 2164337638ns, 2164ns/call
regexp: 2177091893ns, 2177ns/call
答案 2 :(得分:1)
我不认为两个代码具有相同的功能。
第一个代码从forumContent中删除sidPattern中的所有字符。 第二个代码从forumContnt中删除sidPattern字符串,可能不起作用,导致replaceAll()接受该参数作为正则表达式模式。
你确定要删除“&amp; sid =”而不是“&amp; sid = 3a4043284674572e35881e022c68fcd8”吗?
无论如何,我认为String很好,List有点重。