Home > Blockchain >  Problem with writing the first line to a CSV file
Problem with writing the first line to a CSV file

Time:06-22

I'm using a thread to run one function of my code, here's a snippet:

with concurrent.futures.ThreadPoolExecutor() as executor:
    futures = []
    for addresses in ipa_str_s:
        futures.append(executor.submit(checking_connection, ip_address=addresses))
    for future in concurrent.futures.as_completed(futures):
        save_result = (future.result())
        saving_statistics(save_result, saving)

the variable ipa_str_s has a list of IP addresses.

the saving_statistics function, which waits for each call of the checking_connection function to give me the opportunity to save the result.

Called function saving_statistics:

def saving_statistics(save_result, saving):
    with open(saving, 'a', encoding='utf-8') as csv_file:
        csv_writer = csv.writer(csv_file, delimiter=';')
        csv_writer.writerow(['IP-address', 'Packets_transmitted', 'Packets_received'])
        csv_writer.writerow(save_result)

if specify the mode a, then get this result:

IP-address;Packets_transmitted;Packets_received
192.168.1.1;3;3
IP-address;Packets_transmitted;Packets_received;
192.168.1.2;3;0
IP-address;Packets_transmitted;Packets_received;
192.168.1.3;3;0

if specify the mode w, then get this result:

IP-address;Packets_transmitted;Packets_received
192.168.1.3;3;0

Could you tell me please, how can I come to the normal content of a file like this:

IP-address;Packets_transmitted;Packets_received
192.168.1.1;3;3
192.168.1.2;3;0
192.168.1.3;3;0

Thank you very much!

CodePudding user response:

Your function writes the first line (IP-address;Packets_transmitted;Packets_recieved) every time the function saving_statistics gets called.

I see three solutions:

  1. Open the file elsewhere and write the first line, then call the function as needed. Close the file when the program terminates.
  2. Have a variable that is True when the function is first called, and then have it changed to false. Use this to determine whether or not to print the header line
  3. Save the ip addresses in a list object, and write all of them when the program terminates.

Depending on how long you want it to run changes the best option. If you plan for it to run a while, I would do 1 and have a file.close() at the end of your main(), or however you are running this. If it is a short number of items, 3 may be the easiest. 2 will result in the least amount of code change.

CodePudding user response:

FWIW, in most cases, CSV files should not be appended to: they should be written from top-to-bottom once. Log files are a good candidate for appending.

But enough of my philosophy.

You'll at least need to check in saving_statistics to see if the file exists, and if it doesn't add the header and write the row you have. If it does exist you can then go ahead and append:

import csv
from os.path import exists


def saving_statistics_append(save_result, saving):
    csv_writer = None
    if not exists(saving):
        csv_file = open(saving, "w", newline="")
        csv_writer = csv.writer(csv_file, delimiter=";")
        csv_writer.writerow(["IP-address", "Packets_transmitted", "Packets_received"])
    else:
        csv_file = open(saving, "a", encoding="utf-8")
        csv_writer = csv.writer(csv_file, delimiter=";")

    csv_writer.writerow(save_result)
    csv_file.close()

Here's how'd I do this, without append:

def saving_statistics_refresh(save_result, saving):
    prev_rows = []
    if exists(saving):
        with open(saving, newline="") as f:
            reader = csv.reader(f, delimiter=";")
            next(reader)  # discard prev header
            prev_rows = list(reader)

    with open(saving, "w", newline="") as f:
        writer = csv.writer(f, delimiter=";")
        writer.writerow(["IP-address", "Packets_transmitted", "Packets_received"])
        writer.writerows(prev_rows)
        writer.writerow(save_result)

Calling either of those repeatedly with:

saving_statistics_append(["127.0.0.0", 0, 10], "data.csv")
saving_statistics_append(["127.0.0.0", 1110, 99], "data.csv")
saving_statistics_refresh(["127.0.0.0", 333, 444], "data.csv")
saving_statistics_refresh(["127.0.0.0", 77777, 88888], "data.csv")

I get:

IP-address;Packets_transmitted;Packets_received
127.0.0.0;0;10
127.0.0.0;1110;99
127.0.0.0;333;444
127.0.0.0;77777;88888

You can use the DictReader/Writer to help enforce the structure of the CSV:

from collections import namedtuple

Packet_log = namedtuple(
    "Packet_log",
    ["IP_address", "Packets_transmitted", "Packets_received"],
)


def saving_statistics_dict(save_result: Packet_log, saving):
    prev_rows = []
    if exists(saving):
        with open(saving, newline="") as f:
            reader = csv.DictReader(f, delimiter=";")
            prev_rows = list(reader)

    with open(saving, "w", newline="") as f:
        writer = csv.DictWriter(f, delimiter=";", fieldnames=Packet_log._fields)
        writer.writeheader()
        writer.writerows(prev_rows)
        writer.writerow(save_result._asdict())


saving_statistics_append(["127.0.0.0", 0, 10], "data.csv")
saving_statistics_append(["127.0.0.0", 1110, 99], "data.csv")
saving_statistics_dict(Packet_log("127.0.0.0", 333, 444), "data.csv")
saving_statistics_dict(Packet_log("127.0.0.0", 77777, 88888), "data.csv")
  • Related