简体   繁体   中英

Speed up python code

I have some text file in following format (network traffic collected by tcpdump ):

1505372009.023944 00:1e:4c:72:b8:ae > 00:23:f8:93:c1:af, ethertype IPv4 (0x0800), length 97: (tos 0x0, ttl 64, id 5134, offset 0, flags [DF], proto TCP (6), length 83)
    192.168.1.53.36062 > 74.125.143.139.443: Flags [P.], cksum 0x67fd (correct), seq 1255996541:1255996572, ack 1577943820, win 384, options [nop,nop,TS val 356377 ecr 746170020], length 31
    0x0000:  0023 f893 c1af 001e 4c72 b8ae 0800 4500  .#......Lr....E.
    0x0010:  0053 140e 4000 4006 8ab1 c0a8 0135 4a7d  .S..@.@......5J}
    0x0020:  8f8b 8cde 01bb 4adc fc7d 5e0d 830c 8018  ......J..}^.....
    0x0030:  0180 67fd 0000 0101 080a 0005 7019 2c79  ..g.........p.,y
    0x0040:  a6a4 1503 0300 1a00 0000 0000 0000 04d1  ................
    0x0050:  c300 9119 6946 698c 67ac 47a9 368a 1748  ....iFi.g.G.6..H
    0x0060:  1c                                       .

and want to change it to:

1505372009.023944 
    000000:  00 23 f8 93 c1 af 00 1e 4c 72 b8 ae 08 00 45 00  .#......Lr....E.
    000010:  00 53 14 0e 40 00 40 06 8a b1 c0 a8 01 35 4a 7d  .S..@.@......5J}
    000020:  8f 8b 8c de 01 bb 4a dc fc 7d 5e 0d 83 0c 80 18  ......J..}^.....
    000030:  01 80 67 fd 00 00 01 01 08 0a 00 05 70 19 2c 79  ..g.........p.,y
    000040:  a6 a4 15 03 03 00 1a 00 00 00 00 00 00 00 04 d1  ................
    000050:  c3 00 91 19 69 46 69 8c 67 ac 47 a9 36 8a 17 48  ....iFi.g.G.6..H
    000060:  1c                                               .

Here is what I have done:

import re
regexp_time =re.compile("\d\d\d\d\d\d\d\d\d\d.\d\d\d\d\d\d+")
regexp_hex = re.compile("(\t0x\d+:\s+)([0-9a-f ]+)+  ")

with open ('../Traffic/traffic1.txt') as input,open ('../Traffic/txt2.txt','w') as output:
    for line in input:
        if regexp_time.match(line):
            output.write ("%s\n" % (line.split()[0]))
        elif regexp_hex.match(line):
            words = re.split(r'\s{2,}', line)
            bytes=""
            for byte in words[1].split():
                if len(byte) == 4:
                    bytes += "%s%s %s%s "%(byte[0],byte[1],byte[2],byte[3])
                elif len(byte) == 2:
                    bytes += "%s%s "%(byte[0],byte[1])
            output.write ("%s  %s %s \n" % (words[0].replace("0x","00"),"{:<47}".format (bytes),words[2].replace("\n","")))

input.close()
output.close()

Could some one help me in speed up?

Edit

Here is the new version of code depends on @Austin answer, It really speed up the code.

with open ('../Traffic/traffic1.txt') as input,open ('../Traffic/txt1.txt','w') as output:
for line in input:
    if line[0].isdigit():
        output.write (line[:16])
        output.write ('\n')
    elif line.startswith("\t0x"):#(Since there is line which is not hex and not start with timestamp I should check this as well)
        offset = line[:10]  # "    0x0000:  "
        words = line[10:51]  # "0023 f893 c1af 001e 4c72 b8ae 0800 4500 "
        chars = line[51:]  # "  .#......Lr....E."
        line = [offset.replace('x', '0', 1)]
        for a,b,c,d,space in zip (words[0::5],words[1::5],words[2::5],words[3::5],words[4::5]):
            line.append(a)
            line.append(b)
            line.append(space)
            line.append(c)
            line.append(d)
            line.append(space)
        line.append (chars)
        output.write (''.join (line))
input.close()
output.close()

Here is the result:

1505372009.02394
000000:  00 23 f8 93 c1 af 00 1e 4c 72 b8 ae 08 00 45 00 .#......Lr....E.
000010:  00 53 14 0e 40 00 40 06 8a b1 c0 a8 01 35 4a 7d .S..@.@......5J}
000020:  8f 8b 8c de 01 bb 4a dc fc 7d 5e 0d 83 0c 80 18 ......J..}^.....
000030:  01 80 67 fd 00 00 01 01 08 0a 00 05 70 19 2c 79 ..g.........p.,y
000040:  a6 a4 15 03 03 00 1a 00 00 00 00 00 00 00 04 d1 ................
000050:  c3 00 91 19 69 46 69 8c 67 ac 47 a9 36 8a 17 48 ....iFi.g.G.6..H
000060:  1c                                              .

You haven't specified anything else about your file format, including what if any lines appear between blocks of packet data. So I'm going to assume that you just have paragraphs like the one you show, jammed together.

The best way to speed up something like this is to reduce the extra operations. You have a bunch! For example:

  1. You use a regex to match the "start" line.

  2. You use a split to extract the timestamp from the start line.

  3. You use a %-format operator to write the timestamp out.

  4. You use a different regex to match a "hex" line.

  5. You use more than one split to parse the hex line.

  6. You use various formatting operators to output the hex line.

If you're going to use regular expression matching, then I think you should just do one match. Create an alternate pattern (like a|b ) that describes both lines. Use match.lastgroup or .lastindex to decide what got matched.

But your lines are so different that I don't think a regex is needed. Basically, you can decide what sort of line you have by looking at the very first character:

if line[0].isdigit():
    # This is a timestamp line
else:
    # This is a hex line

For timestamp processing, all you want to do is print out the 17 characters at the start of the line: 11 digits, a dot, and 6 more digits. So do that:

if line[0].isdigit():
    output.write(line[:17], '\n')

For hex line processing, you want to make two kinds of changes: you want to replace the 'x' in the hex offset with a zero. That's easy:

    hexline = line.replace('x', '0', 1)   # Note: 1 replacement only!

Then, you want to insert spaces between the groups of 4 hex digits, and pad the short lines so the character display appears in the same column.

This is a place where regular expression replacement might help you. There's a limited number of occurrences, but it may be that the overhead of the Cpython interpreter costs more than the setup and teardown for a regex replacement. You probably should do some profiling on this.

That said, you can split the line into three parts. It's important to capture the trailing space on the middle part, though:

offset = line[:13]   # "    0x0000:  "
words  = line[13:53] # "0023 f893 c1af 001e 4c72 b8ae 0800 4500 "
chars  = line[53:]   # "  .#......Lr....E."

You already know how to replace the 'x' in the offset, and there's nothing to be done to the chars portion of the line. So we'll leave those alone. The remaining task is to spread out the characters in the words string. You can do that in various ways, but it seems easy to process the characters in chunks of 5 (4 hex digits plus a trailing space).

We can do this because we captured the trailing space on the words part. If not, you might have to use itertools.zip_longest(..., fill_value='') , but it's probably easier just to grab one more character.

With that done, you can do:

for a,b,c,d,space in zip(words[0::5], words[1::5], words[2::5], words[3::5], words[4::5]):
    output.write(a, b, space, c, d, space)

Alternatively, instead of making all those calls you could accumulate the characters in a buffer and then write the buffer one time. Something like:

    line = [offset]
    for ...:
        line.extend(a, b, space, c, d, space)
    line.append(chars)
    line.append('\n')
    output.write(''.join(line))

That's fairly straightforward, but like I said, it may not perform quite as well as a regular-expression replacement. That would be due to the regex code running as "C" rather than python bytecode. So you should compare it against a pattern replacement like:

words = re.sub(r'(..)(..) ', '\1 \2 ', words)

Note that I didn't require hex digits, in order to cause any trailing "padding" spaces on the last line of a paragraph to expand in proportion. Again, please check the performance against the zip version above!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM