

先日質問させていただきましたCSV出力について（[前回の質問リンク先](https://ja.stackoverflow.com/questions/80728/python-
csv%E5%87%BA%E5%8A%9B-%E3%81%AB%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E8%AA%AD%E3%81%BF%E8%BE%BC%E3%81%BF-%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6)）では、理解しないまま、取り合えず動く状態にしたという状態のため、理解が伴っておりませんでした。

これでは、上達しないと思い、テキスト処理やループ、リスト等をしっかりと理解し  
少しでも上達したいと思い、再度、質問させていただきます。  
（先日の皆様の回答、アドバイスは、私には高度すぎました。。。）

そこで、下記条件①②までは、自分なりになんとか処理できましたが、  
その結果を、どの様にCSV形式にしていけばよいかが分かりません。  
一旦、辞書形式にする方が良いのか、辞書形式にする場合、ループ処理を使うのか、  
それとも、：で分割してみるのが良いのか・・・等々、ここから、どの様に  
CSV形式にすればよいか、分からない状態です。  
つきましては、再度、アドバイスを頂ければと思います。

条件  
①システムに 1 インターフェイスがあります: を削除  
② ホストされたネットワークの状態: 利用不可 を削除  
③名前からプロファイル名までをCSV形式へ変換し、sample.csvとして保存

[code]

    システムに 1 インターフェイスがあります:
    
        名前                   : Wi-Fi
        説明                   : Qualcomm Atheros QCA61x4A Wireless Network Adapter
        GUID                   : 000000000000000000
        物理アドレス           : xx:xx:xx:xx:xx:xx
        状態                   : 接続されました
        BSSID                  : xx:xx:xx:xx:xx:xx
        ネットワークの種類     : インフラストラクチャ
        無線の種類             : 802.11n
        認証                   : WPA2-パーソナル
        暗号                   : CCMP
        接続モード             : 自動接続
        チャネル               : 6
        受信速度 (Mbps)        : 144.4
        送信速度 (Mbps)        : 144.4
        シグナル               : 94%
        プロファイル           : sample-wifi
    
        ホストされたネットワークの状態: 利用不可
    
[/code]

[code]

    # readlines()でリスト形式で読み込む
    
    with open('example.txt', encoding='utf-8') as f:
        contents = f.readlines()
    
    # ①システムに 1 インターフェイスがあります:
    # ② ホストされたネットワークの状態: 利用不可
    # を削除するため、上記①②が何番目のリストになるか確認
    
    count = 0
    for line in contents:
        print(f"{count} -> {line.strip()}")
        count += 1
    
    # ①②は、0番目と19番目になるため、それ以外を
    # 表示（取り出すため） for new_line in contents[2:18]とする。
    
    for new_line in contents[2:18]:
        print(new_line.strip())
    
[/code]


CC BY-SA 4.0
2021-08-18T06:17:37.373
80886
2021-08-18T23:44:35.613
2021-08-18T06:51:40.467
3060
question
python python3
python CSV出力（に使用するデータの読み込み）について Part2


> # ①システムに 1 インターフェイスがあります:  
>  # ② ホストされたネットワークの状態: 利用不可  
>  # を削除するため、上記①②が何番目のリストになるか確認

Python の場合、リストのインデックスに負の整数値を指定することができます。意味としてはリストの末尾から先頭へ向かっての順番になります。

[code]

    >>> lst = [1, 2, 3, 4, 5, 6]
    >>> lst[-1]   # 末尾の要素
    6
    >>> lst[-2]   # 末尾から先頭へ向かって 2 番目の要素
    5
    >>> lst[2:-2] # 「先頭から 3 番目の要素」から「末尾から先頭へ向かって 3 番目の要素」まで
    [3, 4]
    
[/code]

つまり、以下の様に書いても同じ結果になります。

[code]

    with open('example.txt', encoding='utf-8') as f:
        contents = f.readlines()
    
    for new_line in contents[2:-2]:
        print(new_line.strip())
    
[/code]

もっとも、`netsh wlan show interface` コマンドの出力形式を考慮すれば「空行(blank
line)」で区切られた領域を抽出する」ことになるので以下の様にも書けます。この場合、`contents`
の中身はリストではなく改行コードを含む文字列になります。

[code]

    import re
    
    with open('example.txt', encoding='utf-8') as f:
      contents = f.read()
    
    contents = re.split(r'\r?\n\r?\n', contents)[1]
    
[/code]

**ConfigParser**

> その結果を、どの様にCSV形式にしていけばよいかが分かりません。

`netsh` コマンドの出力は基本的に Windows の INI ファイルと同様の構造で構成されています。違いは INI ファイルではパラメータを
`name=value` 形式で表記しますが、`netsh` では `property : value` になります。また、INI
ファイルにはセクション(`[group_name]`)がありますが、`netsh`
にはありません。その他にも違いはありますが、今回の場合は影響しませんので言及しません。

Python では、この INI ファイルと同様の構造を持つファイルをパース(解析)するためのモジュールが提供されています。

[configparser ---
設定ファイルのパーサー](https://docs.python.org/ja/3/library/configparser.html#module-
configparser)

> このモジュールは、 Microsoft Windows の INI ファイルに似た構造を持ったベーシックな設定用言語を実装した ConfigParser
> クラスを提供します。このクラスを使ってユーザーが簡単にカスタマイズできる Python プログラムを作ることができます

今回の場合の実装例を以下に示します。

[code]

    import configparser
    import csv
    import re
    
    with open('example.txt', encoding='utf-8') as f:
        contents = f.read()
    
    # 「名前」から「プロファイル」までを抽出
    contents = re.split(r'\r?\n\r?\n', contents)[1]
    
    dummy = 'dummy'
    # デリミタを :(コロン)に設定
    parser = configparser.RawConfigParser(delimiters=(':'))
    # contents には INI ファイルの様な「セクション」がないので
    # ダミーセクションを付加
    parser.read_string(f'[{dummy}]\n' + contents)
    # 行と列を入れ替える(transpose)
    table = [*zip(*(parser[dummy].items()))]
    
    # CSV 形式で保存
    with open('sample.csv', 'w', encoding='utf-8') as c:
      cw = csv.writer(c, quoting=csv.QUOTE_MINIMAL)
      cw.writerows(table)
    
[/code]

**sample.csv**

名前 | 説明 | guid | 物理アドレス | 状態 | bssid | ネットワークの種類 | 無線の種類 | 認証 | 暗号 | 接続モード |
チャネル | 受信速度 (mbps) | 送信速度 (mbps) | シグナル | プロファイル  
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---  
Wi-Fi | Qualcomm Atheros QCA61x4A Wireless Network Adapter | 0 |
xx:xx:xx:xx:xx:xx | 接続されました | xx:xx:xx:xx:xx:xx | インフラストラクチャ | 802.11n |
WPA2-パーソナル | CCMP | 自動接続 | 6 | 144.4 | 144.4 | 94% | sample-wifi


CC BY-SA 4.0
2021-08-18T16:26:23.367
80894
2021-08-18T20:46:31.600
2021-08-18T20:46:31.600
47127
47127
80886
answer


①、②はできてるようなので、そこまでのコードを使って続きを考えてみます。

「名前 : Wi-Fi」の行はCSVにあたる  
「ヘッダ行の1列：データ行の1列」という感じで別れています。  
これは最終的に出力するときは、別々の2行で出力されるので、分けて考えます。  
こういうデータを作っていくことを目指します。

[code]

    header_list=["名前", "説明", "GUID"]
    data_list=["Wi-Fi", "Qualcomm Atheros QCA61x4A Wireless Network Adapter", "000000000000000000"]
    
[/code]

この2つのリストができれば、CSV出力は","で連結すれば良いです。

[code]

    print(",".join(header_list))
    print(",".join(data_list))
    
[/code]

forループの中で1行読み込んだnew_line「名前 : Wi-Fi」を  
"名前"と"Wi-Fi"に分割するにはどうするかを考えたら、":"で分割されていることがわかります。  
これを分けるには、splitが使えます

[code]

    header= new_line.split(":")[0] # :で分割した0番目
    data= new_line.split(":")[1]   # :で分割した1番目
    
[/code]

これで分割できているかみてみます。

[code]

    for new_line in contents[2:18]:
        new_line = new_line.strip()
        header= new_line.split(":")[0]
        data= new_line.split(":")[1]
        print(f"header={header} / data={data}")
    
[/code]

このように出力されるので余分な空白がついていることがわかります。

[code]

    "header=名前                    / data= Wi-Fi"
    
[/code]

これもstripで除去します。

[code]

    header= new_line.split(":")[0].strip()
    data= new_line.split(":")[1].strip()
    
[/code]

見ると、物理アドレスの部分は、dataに":"が含まれているために短くなってしまいます。

[code]

    header=物理アドレス / data=xx
    
[/code]

splitをする場合に、何回分割するかを指定する引数[maxsplit](https://docs.python.org/ja/3/library/stdtypes.html?highlight=split#str.split)がありますのでそれを使用します。

[code]

    header= new_line.split(":", 1)[0].strip()
    data= new_line.split(":", 1)[1].strip()
    
[/code]

これで分割は正しく出来ました。

[code]

    header=物理アドレス / data=xx:xx:xx:xx:xx:xx
    
[/code]

あとはこれをheader_list, data_listに入れていきます。

[code]

    header_list = []
    data_list = []
    for new_line in contents[2:18]:
        new_line = new_line.strip()
        header= new_line.split(":", 1)[0].strip()
        data= new_line.split(":")[1].strip()
        print(f"header={header} / data={data}")
    
        header_list.append(header)
        data_list.append(data)
    
[/code]

これで、CSV列の分割と、ヘッダ行、データ行を分けることが出来たのであとは出力するだけです！

「どうやってCSV形式で出力するか」を理解するためすこし冗長なコードになってます。  
ライブラリを使うことでもっと簡単に書けるかと思いますが、今回の質問はアルゴリズムを理解することだと思いますので、ライブラリは使いませんでした。  
個人的には、①、②も行番号で判断するのは、行数が変わったときに問題になるので含まれる文字列で除外したほうがいいと思いました。


CC BY-SA 4.0
2021-08-18T23:44:35.613
80897
2021-08-18T23:44:35.613
28626
80886
answer