[C++] 97 英字有無の判定と取り出し 構造体 struct

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

文字列内に英字があるかどうかの判定とその取り出しができる関数を作成しました。structデータを使いたかったので作ってみました。

Javaでもclassにてstructのようなデータの集合体を作ることができます。これまでJavaでは配列を作って関数の戻り値にしていましたが、データ型を揃える必要があり不便に思っていました。

#include <cppstd.h> // 自製c++標準ライブラリ群

struct Result{
    bool judge;
    string alphabet;
};

Result alphabetExtract(string str)
{
    std::regex pattern("[a-zA-Z]+");
    std::smatch sm;
    if (std::regex_search(str, sm, pattern)){
        string ch = sm.str();
        struct Result res = {true, ch};
        return res;
    }else{
        struct Result res = {false, ""};
        return res;
    }
}

int main()
{
    setlocale(LC_CTYPE, "");

    string test = "123ハローわぁるどHello";
    struct Result res = alphabetExtract(test);
    string alp = res.alphabet;

    cout << "判定結果 " << res.judge << endl;
    cout << "英字 " << alp.c_str() << endl;
}
--------------------------------------------------
出力
--------------------------------------------------
判定結果 1
英字 Hello

[C++] 96 文字列から各文字種を取り出す動的ライブラリ dylib

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

文字列から数字、英字、ひらがな、カタカナ、漢字をそれぞれ取り出す動的ライブラリを作成しました。

ただし最初の単語しか認識せず、2番目以降は取り出しできない限定機能です。文字列全体からもれなく取り出すのであればPythonの方が簡単に書けそうです。

このような便利な動的ライブラリを作成していくとおのずと主言語はC++になっていくでしょう。

#include <cppstd.h> // 自製c++標準ライブラリ群

class CharExtract{
public:
string narrow(const wstring &src);
wstring wide(const string &src);
string extract(string str,int num);
};
#include <cppstd.h> // 自製c++標準ライブラリ群
#include <CharExtract.h>

string hiragana;
string expr;

string CharExtract::narrow(const wstring &src) {
	char *mbs = new char[src.length() * MB_CUR_MAX + 1];
	wcstombs(mbs, src.c_str(), src.length() * MB_CUR_MAX + 1);
	return mbs;
}

wstring CharExtract::wide(const string &src) {
	wchar_t *wcs = new wchar_t[src.length() + 1];
	mbstowcs(wcs, src.c_str(), src.length() + 1);
	return wcs;
}

string CharExtract::extract(string str, int num){
    if (num == 0){
        expr = "[0-9]+"; // 数字
    } else if (num == 1){
        expr = "[a-zA-Z]+"; // 英字
    } else if (num == 2){
        expr = "[\\u3041-\\u309F]+"; // ひらがな
    } else if (num == 3) {
        expr = "[\\u30A0-\\u30FF]+"; // カタカナ
    } else if (num == 4){
        expr = "[\\u4E00-\\u9FFF]+"; // 漢字
    }

    wstring wstr = wide(str);
    wstring wexpr = wide(expr);

    std::wregex we(wexpr);
    std::wsmatch wm;
    if(std::regex_search(wstr, wm, we)){
        character = narrow(wm.str());
    }
    return character;
}
clang++ -dynamiclib -o CharExtract.dylib \
CharExtract.cpp \
-I/code/cpp/mylib/include -std=c++17
#include <cppstd.h> // 自製c++標準ライブラリ群
#include <CharExtract.h>

CharExtract CE;

int main()
{
    setlocale(LC_CTYPE, "");

    string test = 日本語ハローわーるどHelloはろー[123]";
    
    string number = CE.extract(test, 0);
    string alphabet = CE.extract(test, 1);
    string hiragana = CE.extract(test, 2);
    string katakana = CE.extract(test, 3);
    string kanji = CE.extract(test, 4);

    cout << "数字 " << number << endl;
    cout << "英字 " << alphabet << endl;
    cout << "ひらがな " << hiragana << endl;
    cout << "カタカナ " << katakana << endl;
    cout << "漢字 " << kanji << endl;
}
--------------------------------------------------
出力
--------------------------------------------------
数字 123
英字 Hello
ひらがな わ     // 長音"ー"はカタカナ扱い
カタカナ ハロー
漢字 日本語

[C++] 95 文字列からひらがなを取り出す wregex

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

前回は開発速度を優先して泥臭い手法を用いましたが、今回はよりプログラマらしい内容に書き直しました。

対象文字列と全てのひらがなをstringからwstringに変換し、正規表現を使って対象文字列からひらがなを取り出しました。ひらがな設定は文字列リテラルを書き出すのではなくunicodeを使っています。

setlocaleでロケールを設定しないとひらがなを扱えないので要注意です。

マルチバイト文字の扱いにくさはC/C++の弱点の一つですね。Visual C++やC#など、Microsoftはその辺のフォローが行き届いているように思います。まあそれでもWindowsをメインOSにしようとは思いませんが..

#include <cppstd.h> // 自製c++標準ライブラリ群

string hiragana;

string narrow(const wstring &src) {
	char *mbs = new char[src.length() * MB_CUR_MAX + 1];
	wcstombs(mbs, src.c_str(), src.length() * MB_CUR_MAX + 1);
	return mbs;
}

wstring wide(const string &src) {
	wchar_t *wcs = new wchar_t[src.length() + 1];
	mbstowcs(wcs, src.c_str(), src.length() + 1);
	return wcs;
}

string hiragana_extract(string str){
    string expr = "[\\u3041-\\u309F]+"; // ひらがな

    wstring wstr = wide(str);
    wstring wexpr = wide(expr);

    std::wregex we(wexpr);
    std::wsmatch wm;
    if(std::regex_search(wstr, wm, we)){
        hiragana = narrow(wm.str());
    }
    return hiragana;
}

int main()
{
    setlocale(LC_CTYPE, "");

    string test = "水浅葱 みずあさぎhello [123]";
    
    string hiragana = hiragana_extract(test);

    cout << "ひらがな抽出 " << hiragana << endl;
}
--------------------------------------------------
出力
--------------------------------------------------
ひらがな抽出 みずあさぎ

参考サイト1
参考サイト2

[C++] 94 文字列からひらがなを取り出す

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

日本語はマルチバイト文字なので文字列の扱いが難しいです。

今回は漢字とひらがなが混ざった文字列からひらがなだけを取り出しました。

#include <cppstd.h> // 自製c++標準ライブラリ群

vector<string> hiragana_list = 
    {
    "あ","い","う","え","お",
    "か","き","く","け","こ",
    "さ","し","す","せ","そ",
    "た","ち","つ","て","と",
    "な","に","ぬ","ね","の",
    "は","ひ","ふ","へ","ほ",
    "ま","み","む","め","も",
    "や","ゆ","よ",
    "ら","り","る","れ","ろ",
    "わ","を","ん",
    "が","ぎ","ぐ","げ","ご",
    "ざ","じ","ず","ぜ","ぞ",
    "だ","ぢ","づ","で","ど",
    "ば","び","ぶ","べ","ぼ",
    "ぱ","ぴ","ぷ","ぺ","ぽ",
    "ゃ","ゅ","ょ","っ"
    };

vector<string> str_kana;

string hiragana_extract(string str)
{
    int pos;
    unsigned char lead; 
    int char_size;

   for (pos = 0; pos < str.size(); pos += char_size) {
        lead = str[pos];

        if (lead < 0x80) {
            char_size = 1;
        } else if (lead < 0xE0) { 
            char_size = 2;
        } else if (lead < 0xF0) {
            char_size = 3;
        } else {
            char_size = 4;
        }

        cout << str.substr(pos, char_size) << endl;
        string moji = str.substr(pos, char_size);
        
        for (auto &item : hiragana_list) {
            if (item == moji) {
                str_kana.push_back(moji);
            }
        }
    }

    // vector内ひらがなを結合
    std::ostringstream os;
    std::copy(str_kana.begin(), str_kana.end(), std::ostream_iterator<string>(os));
    string hiragana = os.str(); 

    return hiragana;
}

int main(int argc, char **argv)
{
    string str = "水浅葱 みずあさぎ";

    string hiragana = hiragana_extract(str);

    cout << "ひらがな抽出 " << hiragana << endl;

    return 0;
}
--------------------------------------------------
出力
--------------------------------------------------
水
浅
葱
 
み
ず
あ
さ
ぎ
ひらがな抽出 みずあさぎ

2022/7/21追記:
以下の記事にてより洗練されたコードに書き直しました。

[C++] 93 FLTK : ラジオボタンの順送り

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

ラジオボタンを順送りして同一色のカラーコードを変換できるようにしました。

現時点でのアプリのサイズは動的ライブラリを合わせて607KBです。Java版の1.3%に相当します。約77分の1です。ただ配布するのであれば安全性を考え、Java版も有力候補になるかと思います。

Java版はアプリ内からclassファイルを簡単に入手できますから、コード解読の観点ではC++の方が難易度は高いです。

なお起動時の消費メモリはJava版が133MB、C++版が19MBになります。

これで基本機能を実装できたので、Java版と入れ替えて普段使いにします。移植に着手してから、だらだら進めて4.5日で仮完成です。

途中で自宅の簡単なスマートホーム化(エアコンの自動制御他)もありましたので、実質4日といったところでしょうか。

void nextType(Fl_Widget*, void*){
    onoff_zero = zero_rbtn->value();
    onoff_sharp = sharp_rbtn->value();
    onoff_hex = hex_rbtn->value();
    onoff_rgb = rgb_rbtn->value();

    if (onoff_zero == 1){
        onoff_zero = zero_rbtn->value(false);
        onoff_sharp = sharp_rbtn->value(true);
        ToSharpConvert();
    } else if (onoff_sharp == 1){
        onoff_sharp = sharp_rbtn->value(false);
        onoff_hex = hex_rbtn->value(true);
        ToHexConvert();
    } else if (onoff_hex == 1){
        onoff_hex = hex_rbtn->value(false);
        onoff_rgb = rgb_rbtn->value(true);
        ToRGBConvert();
    } else {
        onoff_rgb = rgb_rbtn->value(false);
        onoff_zero = zero_rbtn->value(true);
        ToZeroConvert();
    }
}

[C++] 92 クリップボードへコピー MacOS

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

Mac固有のpbcopyコマンドを使って文字列をクリップボードにコピーしました。

pbcopyは標準出力をクリップボードにコピーするのでechoやprintfなどのコマンドにパイプでつなげば簡単にできます。

基本的にはechoでほとんどの文字列をカバーできますが、カッコなどコマンドの一部として認識してしまう文字を含む場合はprintfを使います。

#include <cppstd.h> // 自製c++標準ライブラリ群

int main(int argc, char **argv)
{
    string str = "RGB(100,149,237)";
    
    string cmd = "printf '" + str + "' | pbcopy";
    // カッコがなければ以下でも可能
    // string cmd = "echo " + str + " | pbcopy";

    system(cmd.c_str());
}

[C++] 91 動的ライブラリdylib作成 カラーコード変換

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

カラーコードを変換する動的ライブラリを作成しました。

コンパイラclang++のオプションとして-std=c++17が必要なのに気がつかず、少し手間取りました。何も付けないとC++03以前のレガシーC++としてコンパイルされるようです。

#pragma once
#include <string>
#include <vector>
#include <stdio.h>
#include <ctype.h>
#include <memory>
#include <sstream>
#include <iostream>
#include <unistd.h>
#include <iomanip>

using std::string; using std::vector;
using std::cout; using std::endl;
using std::to_string; using std::stoi;

class ColorConvert{

public:
vector<string> split(string str, char del);

vector<int> ZeroToRGB(string);
vector<int> SharpToRGB(string);
vector<int> HexToRGB(string);

string ZeroToSharp(string);
string ZeroToHex(string);
string SharpToZero(string);
string SharpToHex(string);
string HexToZero(string);
string HexToSharp(string);
string RGBToHex(string);

string RGBToString(vector<int>);
};
#include "ColorConvert.h"

vector<string> ColorConvert::split(string str, char del) {
    int first = 0;
    int last = str.find_first_of(del);
 
    vector<string> result;
 
    while (first < str.size()) {
        string subStr(str, first, last - first);
 
        result.push_back(subStr);
 
        first = last + 1;
        last = str.find_first_of(del, first);
 
        if (last == string::npos) {
            last = str.size();
        }
    }
    return result;
}

vector<int> ColorConvert::ZeroToRGB(string color_zero){
    vector<int> RGBColor;

    string red0 = color_zero.substr(2,2);
    
    int red = stoi(red0, nullptr, 16);
    RGBColor.push_back(red);

    string green0 = color_zero.substr(4,2);
    int green = stoi(green0, nullptr, 16);
    RGBColor.push_back(green);

    string blue0 = color_zero.substr(6,2);
    int blue = stoi(blue0, nullptr, 16);
    RGBColor.push_back(blue);

    return RGBColor;
}

vector<int> ColorConvert::SharpToRGB(string color_sharp){
    vector<int> RGBColor;

    string red0 = color_sharp.substr(1,2);
    int red = stoi(red0, nullptr, 16);
    RGBColor.push_back(red);

    string green0 = color_sharp.substr(3,2);
    int green = stoi(green0, nullptr, 16);
    RGBColor.push_back(green);

    string blue0 = color_sharp.substr(5,2);
    int blue = stoi(blue0, nullptr, 16);
    RGBColor.push_back(blue);

    return RGBColor;
}

vector<int> ColorConvert::HexToRGB(string color_hex){
    vector<int> RGBColor;

    string red0 = color_hex.substr(0,2);
    int red = stoi(red0, nullptr, 16);
    RGBColor.push_back(red);

    string green0 = color_hex.substr(2,2);
    int green = stoi(green0, nullptr, 16);
    RGBColor.push_back(green);

    string blue0 = color_hex.substr(4,2);
    int blue = stoi(blue0, nullptr, 16);
    RGBColor.push_back(blue);

    return RGBColor;
}

string ColorConvert::ZeroToSharp(string color_zero){
    string color_hex = color_zero.erase(0,2);
    cout << "color_hex " << color_hex.c_str() << endl;

    string color_sharp = "#" + color_hex;

    return color_sharp;
}

string ColorConvert::ZeroToHex(string color_zero){
    string color_hex = color_zero.erase(0,2);
    cout << "color_hex " << color_hex.c_str() << endl;

    return color_hex;
}

string ColorConvert::SharpToZero(string color_sharp){
    string color_hex = color_sharp.erase(0,1);
    cout << "color_hex " << color_hex.c_str() << endl;

    string color_zero = "0x" + color_hex;

    return color_zero;
}

string ColorConvert::SharpToHex(string color_sharp){
    string color_hex = color_sharp.erase(0,1);
    cout << "color_hex " << color_hex.c_str() << endl;

    return color_hex;
}

string ColorConvert::HexToZero(string color_hex){
    string color_zero = "0x" + color_hex;

    return color_zero;
}

string ColorConvert::HexToSharp(string color_hex){
    string color_sharp = "#" + color_hex;

    return color_sharp;
}

string ColorConvert::RGBToHex(string color_rgb){
    vector<string> rgb_list = ColorConvert::split(color_rgb, ',');
    vector<string> red00 = ColorConvert::split(rgb_list[0], '(');
    vector<string> blue00 = ColorConvert::split(rgb_list[2], ')');

    string red0 = red00[1];
    string green0 = rgb_list[1];
    string blue0 = blue00[0];
    cout << "red0 " << red0.c_str() << " green0 " << green0.c_str() << " blue0 " << blue0.c_str() <<endl;

    int red = stoi(red0);
    int green = stoi(green0);
    int blue = stoi(blue0);

    // 10進数から16進数へ変換
    std::stringstream ss1;
    ss1 << std::hex << std::setw(2) << std::setfill('0') << red;
    string red_hex = ss1.str();
    cout << red_hex.c_str() << endl;

    std::stringstream ss2;
    ss2 << std::hex << std::setw(2) << std::setfill('0') << green;
    string green_hex = ss2.str();
    cout << green_hex.c_str() << endl;

    std::stringstream ss3;
    ss3 << std::hex << std::setw(2) << std::setfill('0') << blue;
    string blue_hex = ss3.str();
    cout << blue_hex.c_str() << endl;

    string color_hex = red_hex + green_hex + blue_hex;

    return color_hex;
}

string ColorConvert::RGBToString(vector<int> RGBColor){
    int red = RGBColor[0];
    int green = RGBColor[1];
    int blue = RGBColor[2];
    string color_rgb = "RGB(" + to_string(red) + "," + to_string(green) + "," + to_string(blue) + ")";

    return color_rgb;
}
clang++ -dynamiclib -o ColorConvert.dylib \
ColorConvert.cpp \
-I/code/cpp/mylib/include -std=c++17

[C++] 90 10進数から16進数へ変換、大文字へ変換

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

C++で整数を変換するのは初めてかもしれません。大文字へ変換する関数も載せておきます。

C言語とC++のtoupper関数の違いが分からなくて苦労しました。今回はC言語の方を使っています。C++のtoupper関数は上級者向けでしょうか。型推論が効かないため正確に使わないとコンパイラからのダメ出しが止まりません。

またvectorの範囲コンストラクタというものを初めて使いましたが、最初見た時は関数なのか変数なのか文法的に矛盾しているように見えて意味が分かりませんでした。

現時点でアプリのサイズは455KBです。フル機能のJava版は45.6MBですから100分の1に収まっています。起動はもっさりから爆速になりました。実質的リファクタリングの効果もあると思います。

#include <algorithm>

using std::transform; using std::toupper;

// 大文字へ変換する関数
string capitalizeString(string s)
{
    vector<char> s_char(s.begin(), s.end());

    vector<char> s_char_up;
    for (auto && c:s_char){
        char c_up = toupper(c);
        s_char_up.push_back(c_up);
    }

    string str(s_char_up.begin(), s_char_up.end());

    return str;
}

int red = stoi(red0);
int green = stoi(green0);
int blue = stoi(blue0);

// 10進数から16進数へ変換
std::stringstream ss1;
ss1 << std::hex << red;
// 0埋めで2桁にする場合
// ss1 << std::hex << std::setw(2) << std::setfill('0') << red;
string red_hex = ss1.str();
cout << red_hex.c_str() << endl;

std::stringstream ss2;
ss2 << std::hex << green;
string green_hex = ss2.str();
cout << green_hex.c_str() << endl;

std::stringstream ss3;
ss3 << std::hex << blue;
string blue_hex = ss3.str();
cout << blue_hex.c_str() << endl;

// 大文字へ変換
string color_hex0 = red_hex + green_hex + blue_hex;
string color_hex = capitalizeString(color_hex0);
string color_zero = "0x" + color_hex;
code_input->value("");
code_input->insert(color_zero.c_str());
--------------------------------------------------
出力例
--------------------------------------------------
0xC9171E

[C++] 89 FLTK : ウィジェットの再描画 redraw関数

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

カラーボタンから取得したカラーコードをFl_Boxの色設定に使いました。redraw関数による再描画が必要になります。

extern Fl_Input *name_input, *romaji_input, *code_input;
extern Fl_Tabs *tabs;
extern string colorList[3][140];
extern string colorList2[4][3][120];
extern vector<string> tab_names;
extern Fl_Box *colorBox;
string *name, *romaji, *color;
const char *name_c, *romaji_c, *color_c;

vector<int> RGBConvert(string colorcode){
    vector<int> RGBColor;

    string red0 = colorcode.substr(2,2);
    int red = stoi(red0, nullptr, 16);
    RGBColor.push_back(red);

    string green0 = colorcode.substr(4,2);
    int green = stoi(green0, nullptr, 16);
    RGBColor.push_back(green);

    string blue0 = colorcode.substr(6,2);
    int blue = stoi(blue0, nullptr, 16);
    RGBColor.push_back(blue);

    return RGBColor;
}

void getColor(Fl_Widget*, long num){
    cout << "num " << num << endl;

    Fl_Widget * tab = tabs->value();
    const char* lbl = tab->label();
    cout << "lbl " << lbl << endl;
    string lbl_str = (string)lbl;

    int tab_num = getIndex(tab_names,lbl_str);
    cout << "tab_num " << tab_num << endl;

    if (tab_num == 0){
        name = &(colorList[0][num]);
        name_c = name->c_str();

        romaji_c = "-";

        color = &(colorList[2][num]);
        color_c = color->c_str();
        
    } else {
        name = &(colorList2[tab_num-1][0][num]);
        name_c = name->c_str();

        romaji = &(colorList2[tab_num-1][1][num]);
        romaji_c = romaji->c_str();
        
        color = &(colorList2[tab_num-1][2][num]);
        color_c = color->c_str();
        
    }
    name_input->value("");
    name_input->insert(name_c);

    romaji_input->value("");
    romaji_input->insert(romaji_c);

    code_input->value("");
    code_input->insert(color_c);

    vector<int> rgbcolor = RGBConvert(*color);
    int red = rgbcolor[0];
    int green = rgbcolor[1];
    int blue = rgbcolor[2];
    cout << "RGB " << red << " " << green << " " << blue <<endl;

    colorBox->redraw();
    colorBox->color(fl_rgb_color(red,green,blue));
}

[C++] 88 FLTK : Fl_Tabsのインデックス値取得

[M1 Mac, Big Sur 11.6.7, clang 13.0.0, NO IDE]

Fl_Tabsのインデックス値を取得し、押下したカラーボタンの内容を3次元配列データから取り出すようにしました。

Tabのインデックス値取得はfor文内で変数に代入する方法ではうまくいかず、関数の戻り値にするとあっさり出来ました。

ようやく次の段階に移ることができます。選択した色の表示、カラーコードの変換などです。

extern Fl_Tabs *tabs;
extern string colorList[3][140];
extern string colorList2[4][3][120];
extern vector<string> tab_names;

int getIndex(vector<string> &input, string searched) {
    for (int i = 0; i < input.size(); i++) {
        if (input[i] == searched) {
            return i;
        }
    }
    return -1;
}

void getColor(Fl_Widget*, long num){
    cout << "num " << num << endl;

    Fl_Widget * tab = tabs->value();
    const char* lbl = tab->label();
    cout << "lbl " << lbl << endl;
    string lbl_str = (string)lbl;

    int tab_num = getIndex(tab_names,lbl_str);
    cout << "tab_num " << tab_num << endl;

    if (tab_num == 0){
        string* name = &(colorList[0][num]);
        const char* name_c = name->c_str();
        name_input->value("");
        name_input->insert(name_c);

        const char* romaji_c = "-";
        romaji_input->value("");
        romaji_input->insert(romaji_c);

        string* color = &(colorList[2][num]);
        const char* color_c = color->c_str();
        code_input->value("");
        code_input->insert(color_c);

    } else {
        string* name = &(colorList2[tab_num-1][0][num]);
        const char* name_c = name->c_str();
        name_input->value("");
        name_input->insert(name_c);

        string* romaji = &(colorList2[tab_num-1][1][num]);
        const char* romaji_c = romaji->c_str();
        romaji_input->value("");
        romaji_input->insert(romaji_c);

        string* color = &(colorList2[tab_num-1][2][num]);
        const char* color_c = color->c_str();
        code_input->value("");
        code_input->insert(color_c);
    }
}