[Python] 339 nkfによる文字コード一括変換

[Windows11, Python 3.10.8]

過去記事の更新です。

import glob,subprocess

dir = "対象ディレクトリ"
paths_cpp = glob.glob(dir + "/**/*.cpp", recursive=True)
paths_h = glob.glob(dir + "/**/*.h", recursive=True)

# リストを結合
paths_cpp.extend(paths_h)

print(paths_cpp)
print(len(paths_cpp))

for path in paths_cpp:
    cmd = "nkf -s --overwrite %s" %path
    subprocess.call(cmd, shell=True)
    print("%s 変換完了" %path)

# -s : Shift-JISへ変換
# -w : UTF-8へ変換(BOMなし)
# -w8, -W : UTF-8へ変換(BOM付き)

# 別ファイルに変換の場合
# nkf -w a.cpp > b.cpp

[C++ & Python] 185 FLTK : LLDB補助アプリの製作着手

[M1 Mac, Big Sur 11.6.8, clang 13.0.0, Python 3.10.4, NO IDE]

早いもので前作のビデオツールアプリ完成から20日ほど経過していました。

LLDBから得られたレジスタやメモリのデータをExcelにまとめるツールを作ることにしました。逆アセンブリコード1行ごとのレジスタ値、スタック領域メモリ値を一気にまとめます。

C++とPythonで作成します。役割分担は以下の通りです。

C++ : GUI(FLTK)、LLDBコマンド作成・実行・ログ取得
Python : LLDBログから各データを抽出しExcelファイルにまとめる

まずはPythonから取り組みます。

LLDBログデータ例

[Python] 338 ディレクトリ内ファイルの文字コード一括変換 上書きの場合

[M1 Mac, Big Sur 11.6.8, Python 3.10.4]

Pythonに関する記事は約1ヶ月ぶりです。

最近TCP/IPに関する書籍を購入したのですが、サンプルコードの文字コードがEUC-JPなので以下のコードでUTF-8に一括変換しました。

サンプルコードのディレクトリをコピーして上書き変換させています。過去に類似の記事がありますが、別ファイルへ変換したケースです。

import glob,subprocess

dir = "/sample_code/CSockets_copy"
paths_c = glob.glob(dir + "/**/*.c", recursive=True)
paths_h = glob.glob(dir + "/**/*.h", recursive=True)

paths_c.extend(paths_h)

print(paths_c)
print(len(paths_c))

for path in paths_c:
    cmd = "nkf -w --overwrite %s" %path
    subprocess.call(cmd, shell=True)

[Python]337 文字列から各文字種を取り出すre.findall

[M1 Mac, Big Sur 11.6.7, Python 3.10.4]

[C++]98の記事で作成した関数がappファイルではうまく動いてくれないため、急遽Python版を作成しました。なおアプリの実行ファイルではC++版は正常に動作します。

C++版作成には結構苦労していて正直これ以上いじりたくないので、製作中のFLTKアプリにはこの20行のPython版をモジュール化して導入するつもりです。

C++で日本語を扱うのはなかなか難しいですから、いざとなればPythonの力を借りることにします。

import re
from enum import Enum

class CharType(Enum):
    NUMBER = 1
    ALPHABET = 2
    HIRAGANA = 3
    KATAKANA = 4
    KANJI = 5
 
def CharExtractPy(str, type):
    if type == CharType.NUMBER:
        ch = re.findall('[0-9]+', str)
    elif type == CharType.ALPHABET:
        ch = re.findall('[a-zA-Z]+', str)
    elif type == CharType.HIRAGANA:
        ch = re.findall('[ぁ-ゟ]+', str)
    elif type == CharType.KATAKANA:
        ch = re.findall('[\ァ-ヿ]+', str)
    elif type == CharType.KANJI:
        ch = re.findall('[\u2E80-\u2FDF\u3005-\u3007\u3400-\u4DBF\u4E00-\u9FFF\uF900-\uFAFF\U00020000-\U0002EBEF]+', str)
    
    return ch
   
if __name__ == '__main__':
    
    str = "日本語ハローわぁるどHelloはろぅ[123]"
    
    # 数字
    ch1 = CharExtractPy(str,CharType.NUMBER)
    print("数字")
    print(ch1)
    
    # 英字
    ch2 = CharExtractPy(str,CharType.ALPHABET)
    print("英字")
    print(ch2)
    
    # ひらがな
    ch3 = CharExtractPy(str,CharType.HIRAGANA)
    print("ひらがな")
    print(ch3)
    
    # カタカナ
    ch4 = CharExtractPy(str,CharType.KATAKANA)
    print("カタカナ")
    print(ch4)
    
    # 漢字
    ch5 = CharExtractPy(str,CharType.KANJI)
    print("漢字")
    print(ch5)
--------------------------------------------------
出力
--------------------------------------------------
数字
['123']
英字
['Hello']
ひらがな
['わぁるど', 'はろぅ']
カタカナ
['ハロー']
漢字
['日本語']

[Python]336 2次元リストからひらがなを抽出

[M1 Mac, Big Sur 11.6.7, Python 3.10.4]

漢字とひらがなの混ざった2次元リストからひらがなを抽出してリスト化するスクリプトを書きました。

やはりマルチバイト文字の扱いやすさはスクリプト言語に分があるようです。

import re

<2次元リストは省略>

for color in colors:
    dup_num = 0
    color_hiragana = list()
    for ele in color:
        hiragana = re.findall('[ぁ-ゟ]+', ele)
        if len(hiragana) == 2: # 要素内にひらがな単語が2つある場合は最初のを削除
            del hiragana[0]
            dup_num +=1
        color_hiragana.append(hiragana)
    
    # リストの平滑化
    color_hiragana_flat = [e for l in color_hiragana for e in l]    
    print(dup_num)
    print(len(color_hiragana_flat))
    print(color_hiragana_flat)

[Python]335 重複する画像を削除する 改良版

[M1 Mac, Big Sur 11.6.7, Python 3.10.4]

前回の改良版です。

前回のスクリプトでは、コピーした画像が3枚ある場合、三つ巴になって3枚全て削除対象になる可能性があるため、st_birthtime(元ファイル作成時刻)が同じケースではst_ctime(コピーした時刻)が古い方を削除対象にするようにしました。これで最も新しいコピー画像が残ります。

最初からst_ctimeを判断基準にするとスクリプトがよりシンプルになります。

このスクリプトは先日製作したRustアプリにツールとして登録しました。

import os,glob,itertools,cv2
import numpy as np

path_list = glob.glob("/Desktop/temp/*.png")

delete_list = []
for pair in itertools.combinations(path_list, 2):
    path_pair = list(pair)
    
    image1 = cv2.imread(path_pair[0])
    stat1 = os.stat(path_pair[0])
    btime1 = stat1.st_birthtime
    ctime1 = stat1.st_ctime
    
    image2 = cv2.imread(path_pair[1])
    stat2 = os.stat(path_pair[1])
    btime2 = stat2.st_birthtime
    ctime2 = stat2.st_ctime  

    result_compare = np.array_equal(image1, image2)
        
    if result_compare == True:
        if btime1 > btime2:
            delete_list.append(path_pair[1])
        elif btime1 < btime2:
            delete_list.append(path_pair[0])
        else:
            if ctime1 > ctime2:
                delete_list.append(path_pair[1])
            else:
                delete_list.append(path_pair[0])
                
print(delete_list)

for file in delete_list:
    try:
        os.remove(file)
    except FileNotFoundError:
        pass

[Python]334 重複する画像を削除する

[M1 Mac, Big Sur 11.6.7, Python 3.10.4]

スクリーンショットを何枚も撮影していると全く同じ画像が混ざることがあります。

OpenCVを使って同一ディレクトリ内にある画像同士を比較し内容が同じ場合は作成日時が古い方を削除するスクリプトを作成しました。

ファイルパスのリストを作成し、要素数2の組み合わせについて画像を比較、同じであれば作成日時の古い方を削除リストに追加。全ての判定が終わってからまとめて削除します。同じ画像が3枚以上あっても対応できるようにしました。

前にも書きましたが作成日時 st_birthtimeはMacOS限定です。Windowsの場合はst_ctimeになります。

import os,glob,itertools,cv2
import numpy as np

path_list = glob.glob("/Desktop/temp/*.png")

delete_list = []
for pair in itertools.combinations(path_list, 2):
    path_pair = list(pair)
    
    image1 = cv2.imread(path_pair[0])
    stat1 = os.stat(path_pair[0])
    btime1 = stat1.st_birthtime
    
    image2 = cv2.imread(path_pair[1])
    stat2 = os.stat(path_pair[1])
    btime2 = stat2.st_birthtime    

    result_compare = np.array_equal(image1, image2)
        
    if result_compare == True:
        if btime1 > btime2:
            delete_list.append(path_pair[1])
        else:
            delete_list.append(path_pair[0])
                
print(delete_list)

for file in delete_list:
    try:
        os.remove(file)
    except FileNotFoundError:
        pass

220712追記:
スクリプトの改良版を作成しました。

[Rust] 08 FLTK-RS : Python自製ライブラリのimport 

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

Rustコードに導入するPythonスクリプトが自製ライブラリをimportする場合は、.bash_profileにPYTHONPATHとしてライブラリのあるディレクトリを設定します。

これでランチャーアプリに登録するPythonスクリプトが4つに増えました。

# 導入するPythonスクリプトのあるディレクトリ設定
export PYTHONPATH="/code/Python/library/python_module"

# importする自製ライブラリのあるディレクトリ設定
export PYTHONPATH="/code/Python/library"
use fltk::{app, prelude::*, button::*, window::Window, group::{Group}};
use pyo3::prelude::*;

fn pythonmodule1() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module1 start");
        let mymodule = PyModule::import(py, "OutingCosts")?;
        let response:i32 = mymodule.getattr("outingcosts")?.call0()?.extract()?;
        println!("module1 {}", response);
        Ok(())
    })
}

fn pythonmodule2() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module2 start");
        let mymodule = PyModule::import(py, "PhotoSorting")?;
        let response:i32 = mymodule.getattr("photosorting")?.call0()?.extract()?;
        println!("module2 {}", response);
        Ok(())
    })
}

fn pythonmodule3() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module3 start");
        let mymodule = PyModule::import(py, "Scraping")?;
        let response:i32 = mymodule.getattr("scraping")?.call0()?.extract()?;
        println!("module3 {}", response);
        Ok(())
    })
}

fn pythonmodule4() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module4 start");
        let mymodule = PyModule::import(py, "ScrapingJP")?;
        let response:i32 = mymodule.getattr("scrapingjp")?.call0()?.extract()?;
        println!("module4 {}", response);
        Ok(())
    })
}

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
    
    let  grp = Group::new(15, 15, 285 , 160, "");
    let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "OutingCosts");
    _btn1.set(true);
    let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "PhotoSorting");
    let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "Scraping");
    let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "ScrapingJP");
    let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
    let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
    let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
    let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
    let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
    let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    grp.end();

    let mut _btn = Button::new(300, 15, 50, 25, "実行");
    _btn.set_callback(move |_| {
        let _onoff1 = _btn1.value();let _onoff2 = _btn2.value();let _onoff3 = _btn3.value();
        let _onoff4 = _btn4.value();let _onoff5 = _btn5.value();let _onoff6 = _btn6.value();
        let _onoff7 = _btn7.value();let _onoff8 = _btn8.value();let _onoff9 = _btn9.value();
        let _onoff10 = _btn10.value();

        if _onoff1 == true{
            pythonmodule1();
        } else if _onoff2 == true{
            pythonmodule2();
        } else if _onoff3 == true{
            pythonmodule3();
        } else if _onoff4 == true{
            pythonmodule4();
        }
    });

    window.end();
    window.show();
    app.run().unwrap();
}

[Rust] 07 FLTK-RS : アプリおよびプロジェクトのサイズ

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

ランチャーアプリに2つのPythonスクリプトを登録しました。

アプリ(実行ファイル)とプロジェクトのサイズは以下の通りです。

Rustアプリ:2.4MB
Rustプロジェクト:430MB
比較例(同じような機能数のFLTKアプリ)
C++アプリ:237KB
C++プロジェクト:4MB

アプリは10倍、プロジェクトにおいては100倍以上です。

Gitの他にプロジェクトを丸ごとコピーしてコードを管理していますが、Rustの場合は安易にコピーできないですね。コピー3つで1.3GBですから。

なかなか優れもののRustではありますが、サイズの大きさ、そしてコンパイルの遅さを考えるとまだまだ主言語にはできないな、というのが率直な感想です。

Rust修得に関する今後の課題は以下の通りです。

1.appファイルの作成方法
2.Pythonスクリプトに自製ライブラリのimportがある場合の対応

[Rust] 06 FLTK-RS : ランチャーアプリ製作 PyO3によるPythonモジュール導入

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

Rustコードから引数なしのPythonスクリプトを実行できるようにしました。Python/C API経験者であればそんなに難しくないでしょう。

RustではPyO3ライブラリを使います。PyO3公式サイトを読めば、スクリプト埋め込みや複数スクリプトなど様々なPythonの使い方が分かります。

with_gilのgilはGlobal interpreter lockの略であり、Pythonインタプリタへのアクセスができるようになります。

C++やObjective-Cよりも導入方法が簡単で驚きです。これならPythonを使うのであればC言語系ではなくRustにしますね。以前はセキュリティ面で課題を抱えていたと聞きますが、PyO3の開発陣はすごいなと素直に思いました。

Python→C/C++の流れからPython→RustのようにC/C++をスルーするユーザーが今後増えていくのではないでしょうか。またRustの記事を書く方の中にはC/C++を知らない方もいるということを認識しておく必要があります。

いつも思うのですが、プログラミングに関する記事を書く場合はWindowsなのかLinuxなのかMacOSなのか、バージョンと合わせて明記していただきたいものです。

use fltk::{app, prelude::*, button::*, window::Window, group::{Group}};
use pyo3::prelude::*;

// PhotoSorting.py内のphotosorting関数を使用
fn pythonmodule() -> PyResult<()> {
    Python::with_gil(|py| {
        let mymodule = PyModule::import(py, "PhotoSorting")?;
        let response:i32 = mymodule.getattr("photosorting")?.call0()?.extract()?;
        println!("{}", response);
        Ok(())
    })
}

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
    
    let  grp = Group::new(15, 15, 285 , 160, "");
    let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "PhotoSorting");
    _btn1.set(true);
    let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "");
    let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "");
    let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "");
    let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
    let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
    let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
    let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
    let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
    let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    grp.end();

    let mut _btn = Button::new(300, 15, 50, 25, "実行");
    _btn.set_callback(move |_| {
        let _onoff1 = _btn1.value();let _onoff2 = _btn2.value();let _onoff3 = _btn3.value();
        let _onoff4 = _btn4.value();let _onoff5 = _btn5.value();let _onoff6 = _btn6.value();
        let _onoff7 = _btn7.value();let _onoff8 = _btn8.value();let _onoff9 = _btn9.value();
        let _onoff10 = _btn10.value();

        if _onoff1 == true{
            println!("onoff1");
            pythonmodule();
        }
    });

    window.end();
    window.show();
    app.run().unwrap();
}
[dependencies.pyo3]
version = "0.16.5"
features = ["auto-initialize"]

参考サイト