電脳ヨーグルト(技術ブログ)

勉強したことを淡々とメモしていきます

Python(CGI)で作った簡易単語検索アプリ

CGI (Common Gateway Interface) を用いることで、Webサーバー側でプログラムを動かすことができます。


cgi-binディレクトリを含んだディレクトリ上で

python3-m http.server --cgi 8xxx

と実行することでPythonの簡易Webサーバが起動し、ブラウザからプログラムを確認することができます。

http://localhost:8080/cgi-bin/word_count.py

ブラウザに上のようなURLを入力するとアクセスできます。

また、8080のようなポート番号は任意の数字なので8000番とかでも大丈夫です。


Pythonを使って、小説などの文字がたくさん書かれた文章の中から調べたい単語の個数を数えるプログラムを作りました。

正直Wordの検索機能で代替できるようなゴミアプリですが、暇だったので作ってみました。

青空文庫から太宰治人間失格の文章をとってきて、txtファイルに文章をコピーしました。

ディレクトリ構成は以下のようになっています。

f:id:at25250410:20190428205047p:plain


実際に動かしてみるとこんな感じです。


f:id:at25250410:20190428203848p:plain


f:id:at25250410:20190428203931p:plain


f:id:at25250410:20190428204012p:plain


人間失格の文章が入ったtxtファイルを開いて、ブラウザから入力した文字をfindall()関数で検索します。

検索した単語が入ったリストが生成されるので、それをlen()で個数の値を取得しています。

やってることはかなり単純です。


以下ソースコードです。

#! /usr/bin/env python3
import cgi
import cgitb
import os.path
import html
import re
cgitb.enable()
# 文字化け対策
import sys
import io
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
#自動でutf-8にエンコードされて出力される

FILE_LOG = "nigen-sikkaku.txt"

# 以下関数
def print_html1(body, sum):
    print("Content-Type: text/html; charset=utf-8")
    print("")
    print("""
<html><head><meta charset="utf-8">
<title>数</title></head><body>
<h1>文章の単語の数を数える</h1>
<div><form>
検索したい単語:<input type="text" name="body" size="40">
<input type="submit" value="検索">
<input type="hidden" name="mode" value="serach">
</form></div><hr>
[{0}]は{1}回出現しました。
</body></html>
    """.format(body, sum))
    jump('word_count.py')

def print_html2(sum):
    print("Content-Type: text/html; charset=utf-8")
    print("")
    print("""
<html><head><meta charset="utf-8">
<title>数</title></head><body>
<h1>文章の単語の数を数える</h1>
<div><form>
検索したい単語:<input type="text" name="body" size="20">
<input type="submit" value="検索">
<input type="hidden" name="mode" value="serach">
</form></div><hr>
{0}
</body></html>
    """.format(sum))
    jump('word_count.py')

def mode_read(form):
    log = ""
    if os.path.exists(FILE_LOG):
        with open(FILE_LOG, "r", encoding='utf-8') as f:
            log = f.read()
    print_html2(log)

def jump(url):
    print("")
    print('<html><head>')
    print('<meta http-equiv="refresh" contnt="0;'+url+'">')
    print('</head><body>')
    print('<a href="'+url+'">もう一度検索</a></body></html>')

def mode_serach(form):
    body = form.getvalue("body", "")
    body = html.escape(body)
    log = ""
    if os.path.exists(FILE_LOG):
        with open(FILE_LOG, "r", encoding='utf-8') as f:
            log = f.read()
    matches = re.findall(str(body), log)
    word_sum = len(matches)
    print_html1(str(body), word_sum)

def main():
    form = cgi.FieldStorage() #URLパラメータの取得
    mode = form.getvalue("mode", "read")
    if mode == "read": mode_read(form)
    elif mode == "serach": mode_serach(form)
    else: mode_read(form)

if __name__ == "__main__":
    main()

指定したビットの状態を調べ、値をセットする関数

指定したビットの状態を調べるGetBit関数と指定したビットの値を変更するSetBit関数を作りました。

GetBit関数

0~255までの10進数を8ビットの2進数の文字列に変換し、そのビットの状態を調べる関数。
n番目のビットが1だったらTRUE
n番目のビットが0だったらFALSE

(例)
numが255でbitが3の時
255→11111111
GetBit(num, bit, &onFlg1);
3ビット目は1なのでTRUEを返す

SetBit関数

0~255までの10進数を8ビットの2進数の文字列に変換し、指定したビットの数字を変更する関数。

(例)
numが255でbitが3の時
onFlg2 = FALSE;
255→11111111
SetBit(&num, bit, onFlg2);
onFlg2 = FALSE; なので3ビット目が0に変更される。
num(11111111)がnum(11111011)

#include <stdio.h>
#include <windows.h>
#define STR_LEN (30)
#define ONE_BYTE (8)
/* 関数宣言 */
BOOL GetBit(BYTE num, int bit, BOOL * onFlg);
BOOL SetBit(BYTE * num, int bit, BOOL onFlg);

/* 関数定義 */
BOOL GetBit(BYTE num, int bit, BOOL *onFlg){
	//変数の定義
	DWORD sorce_num, str_len;
	int bin_part;
	char str_f[STR_LEN] = { '\0' };
	sorce_num = num;
	str_len = ONE_BYTE;
	if(bit < 0 || bit > 7){
		printf("bitが0~7の範囲外の場合(Ret = FALSE)\n");
		return FALSE;
	}
	// numを2進数の文字列に変換
	// 変換した文字をstrに格納
	while(str_len > 0) {
		str_len--;
		bin_part = sorce_num % 2;
		if (sorce_num != 0) {
			*(str_f + str_len) = bin_part + 48;
			sorce_num /= 2;
		}
		// 残りの桁を0埋め
		else {
			*(str_f + str_len) = '0';
		}
	}

	*(str_f + ONE_BYTE) = '\0';
	if (str_f[bit] == '1') {
		*onFlg = TRUE;
		printf("GetBit(%d %d %d) ON %s\n", num, bit, *onFlg, str_f);
		return *onFlg;
	}
	else if(str_f[bit] == '0'){
		*onFlg = FALSE;
		printf("GetBit(%d %d %d) OFF %s\n", num, bit, *onFlg, str_f);
		return *onFlg;
	}
	else{
		printf("0か1以外の数字が入ってます");
	}

	if(onFlg == NULL){
		printf("onFlgがNULLの場合(Ret = FALSE)\n");
		return FALSE;
	}
}

/* 関数定義 */
BOOL SetBit(BYTE * num, int bit, BOOL onFlg){
	//変数の定義
	DWORD sorce_num, str_len;
	int bin_part;
	char str_f[STR_LEN] = { '\0' };
	sorce_num = *num;
	str_len = ONE_BYTE;
	if(bit < 0 || bit > 7){
		printf("bitが0~7の範囲外の場合(Ret = FALSE)\n");
		return FALSE;
	}
	// numを2進数の文字列に変換
	// 変換した文字をstrに格納
	while(str_len > 0) {
		str_len--;
		bin_part = sorce_num % 2;
		if (sorce_num != 0) {
				*(str_f + str_len) = bin_part + 48;
				sorce_num /= 2;
		}
		// 残りの桁を0埋め
		else {
			*(str_f + str_len) = '0';
		}
	}
	*(str_f + ONE_BYTE) = '\0';

	if(onFlg == TRUE){
		str_f[ONE_BYTE-bit] = '1';
		printf("SetBit(%d %d %d) ON %s\n", *num, bit, onFlg, str_f);
		return onFlg;
	}
	else if(onFlg == FALSE){
		str_f[ONE_BYTE-bit] = '0';
		printf("SetBit(%d %d %d) OFF %s\n", *num, bit, onFlg, str_f);
		return onFlg;
	}

	if(num == NULL){
		printf("numがNULLの場合(Ret = FALSE)\n");
		return FALSE;
	}
}

void main() {
	BYTE num = 255;
	BOOL onFlg1;
	BOOL onFlg2;
	// bitの数字は0~7
	int bit = 3;
	// 引数で与えたbit番目の文字が1ならonFlg1 = TRUE
	// 引数で与えたbit番目の文字が1ならonFlg1 = FALSE
	GetBit(num, bit, &onFlg1);
	onFlg2 = FALSE;
	// onFlg2 = TRUE の時は、引数で与えたbit番目の文字が1になる
	// onFlg2 = FALSE の時は、引数で与えたbit番目の文字が0になる
	SetBit(&num, bit, onFlg2);
}

与えられた数字を10進、16進、2進の文字列に変換してchar配列に格納する関数

任意の数字を10進、16進、2進の文字列に変換してchar配列に格納する関数を作りました。

3つの関数は与えられら数字をそれぞれ10進数、16進数、2進数の文字列に変換します。

プログラムの中で+48や+55という数字が出てきますが、それは数字を文字列に変換するためのものです。
例えば数字の5を文字列の5として扱いたい場合は数字の5に48を足し、53とすればよいのです。

詳しくは以下のASCIIコード表みてください。
www9.plala.or.jp

#include <stdio.h>
#include <windows.h>
#define STR_LEN (30)
#define STORE_SIZE (16)

/* 関数宣言 */
BOOL MakeIntString(DWORD num, char * str, DWORD len);
BOOL MakeHexString(DWORD num, char * str);
BOOL MakeBinString(BYTE num, char * str);

/* 関数定義 */
BOOL MakeIntString(DWORD num, char * str, DWORD len) {
	//変数の定義
	//入力エラー処理
	DWORD sorce_num, str_len;
	if(str == NULL || len == 0){
	 printf("strがNULLもしくはlenが0(Ret = FALSE)\n");
	 return FALSE;
	}
	sorce_num = num;
	str_len = len;
	// 変換した文字をstrに格納
	while(str_len > 0) {
	 str_len--;
	 if (sorce_num != 0) {
	 // 数字から文字に変換 +48で整数が文字に変わる
		*(str + str_len) = (sorce_num % 10) + 48; //配列の一番右から順に一桁目の文字を入れていく
		sorce_num /= 10;
	 }
	 // 残りの桁を0埋め
	 else {
		*(str + str_len) = '0';
	 }
	}
	*(str + len) = '\0';
	//出力
	printf("MakeIntString(%d, str, %d) → ", num, len);
	if (sorce_num == 0) {
	 printf("str = %s(Ret=TRUE)\n", str);
	 return TRUE;
	}
	else {
	 printf("str = %s(Ret=FALS+E)\n", str);
	 return FALSE;
	}
}

/* 関数定義 */
BOOL MakeHexString(DWORD num, char * str) {
	//変数の定義
	DWORD sorce_num, str_len;
	int hex_part;
	sorce_num = num;
	str_len = STORE_SIZE;
	if(str == NULL){
		printf("strがNULL(Ret = FALSE)\n");
		goto label;
	}
	// 変換した文字をstrに格納
	while(str_len > 0) {
		str_len--;
		hex_part = sorce_num % 16;
		if (sorce_num != 0) {
		 if (hex_part >= 0 && hex_part <= 9){
			*(str + str_len) = hex_part + 48;
		 }
		 // 数字から文字に変換 +55で整数(1~9)が文字に変わる
		 else if(hex_part >= 10 && hex_part <= 15 ){
			*(str + str_len) = hex_part + 55;
		 }
		  // 配列の一番右から順に一桁目の文字を入れていく
		 sorce_num /= 16;
		}
		// 残りの桁を0埋め
		else {
		 *(str + str_len) = '0';
		}
	}

	*(str + STORE_SIZE) = '\0';
	//出力
	printf("MakeHexString(%d, str) → ", num);
	if (sorce_num == 0) {
		printf("str = %s(Ret=TRUE)\n", str);
		return TRUE;
	}
	else {
		printf("str = %s(Ret=FALS+E)\n", str);
		label:
		return FALSE;
	}
}


/* 関数定義 */
BOOL MakeBinString(BYTE num, char * str){
	//変数の定義
	DWORD sorce_num, str_len;
	int bin_part;
	sorce_num = num;
	str_len = STORE_SIZE;
	if(str == NULL){
		printf("strがNULL(Ret = FALSE)\n");
		goto label;
	}

	// 変換した文字をstrに格納
	while(str_len > 0) {
		str_len--;
		bin_part = sorce_num % 2;
		if (sorce_num != 0) {
		  *(str + str_len) = bin_part + 48;
			// 配列の一番右から順に一桁目の文字を入れていく
			sorce_num /= 2;
		}
		// 残りの桁を0埋め
		else {
			*(str + str_len) = '0';
		}
	}
	*(str + STORE_SIZE) = '\0';
	//出力
	printf("MakeBinString(%d, str) → ", num);
	if (sorce_num == 0) {
		printf("str = %s(Ret=TRUE)\n", str);
		return TRUE;
	}
	else {
		printf("str = %s(Ret=FALS+E)\n", str);
		label:
		return FALSE;
	}
}

void main() {
       // 変換する数字の値は任意
	DWORD i = 123456;
	DWORD len = 10;
	// 変換後の文字列を格納するための配列
	char str1[STR_LEN] = { '\0' };
	char str2[STR_LEN] = { '\0' };
	char str3[STR_LEN] = { '\0' };
	// 数字を10進の文字列に変換
	MakeIntString(i, str1, len);
	// 数字を16進の文字列に変換
	MakeHexString(200, str2);
	// 数字を2進の文字列に変換
	MakeBinString(10, str3);
}

【C言語】配列の要素の和を返却する関数

配列data_arrayのすべての要素の和を返す関数です。

# define STR_SIZE (30)
# include <stdio.h>

/* 関数宣言 */
long GetTotal(long * dataArray, unsigned long dataCount);

/* 関数定義 */
long GetTotal(long * dataArray, unsigned long dataCount){
    int i;
    int total = 0;
    for(i = 0; i < dataCount; i++){    
        total += dataArray[i];
    }
    return total;
} 

/* main(処理始まりの関数) */
int main(void)
{
    int result;
    long data_array[] = {1,2,3,4,5,6,7,8,9};
    unsigned long number;
    number = sizeof(data_array) / sizeof(long);    
    result = GetTotal(data_array, number);
    printf("result%d \n", result);

    return 0;
}

【C言語】2つの文字列を連結する関数

2つの文字列を連結する関数です。
コンソールで入力した2つの文字列を引数にして、関数内で連結した文字列を出力します。

 define STR_SIZE (30)
# include <stdio.h>

/* 関数宣言 */
void CatString(char * str1, char * str2);

/* 関数定義 */
void CatString(char * str1, char * str2){
    int i;
    int j;
    for(i = 0; str1[i] != '\0'; i++){    
    }
    for(j = 0; str2[j] != '\0'; j++){       
        str1[i] = str2[j];
        i++;    
    }
    printf("連結された文字列 %s \n",str1);
} 

/* main(処理始まりの関数) */
int main(void)
{
    char str1[STR_SIZE];
    char str2[STR_SIZE];
    printf("str1 ");
    scanf("%s", str1);
    printf("str2 ");
    scanf("%s", str2);
    CatString(str1, str2);

    return 0;
}

【C言語】文字列の長さを返す関数

コンソールで入力した文字列の長さを返す関数です。

#include <stdio.h>

/* 関数宣言 */
unsigned long GetStringLength(char * str);

/* 関数定義 */
unsigned long GetStringLength(char * str){
	int i;
	for (i = 0; str[i] != '\0'; i++);
	return i;
}

int main(void) 
{
	char str[30];
	int result;
	printf("文字列を入力してください。\n");
	scanf("%s", str);
	result = GetStringLength(str);
	printf("文字列の長さ:%d\n", result);

	return 0;
}

OSI参照モデルとは

OSI参照モデルについて簡単にまとめると・・・
・コンピュータの持つべき通信機能を階層構造に分割したモデル!
・通信機能を7つの階層に分けて定義している!


OSI参照モデルってなんですか?

OSI参照モデルっていうのは、通信に必要な機能を7つの階層に分けて、機能を分割することで複雑になりがちなネットワークプロトコルを簡略化するためのモデルだ。


な、七つもあるんですか?


下の図にあるように、物理層データリンク層ネットワーク層トランスポート層、セッション層、プレゼンテーション層、アプリケーション層の7種類があるぜ。

f:id:at25250410:20190210223520p:plain

それぞれどんな役割があるんですか?

まあ待て、順を追って説明するから。

まず第一層物理層は、0や1のビット列を電圧の高低に変換する機能を持ってるな。


第二層データリンク層は0と1で構成されたビット列を意味のあるからまりに分けて相手に伝える役割を果たしてるぜ。


第三層ネットワーク層では、どの経路を通って宛先までデータを届けるかの経路を選択してるな。


第四層トランスポート層では、宛先までデータを確実に届けるという役割があるんだ。


第五層セッション層は、データの通信の確立や切断を管理する役割だ。


第六層プレゼンテーション層では、機器ごとに若干異なるデータ形式をネットワーク共通のデータ形式に変換する役割だな。


そして第七層アプリケーション層は、ファイルの転送や電子メール、遠隔ログインなどをするためのプロトコルがあるんだぜ。


なるほど、この7つの階層が相互に機能することでスムーズな通信が実現できるわけですね!

ちなみに、俺が昔教わったOSI参照モデルの各層の名前の覚え方は、第七層からそれぞれの頭文字をまとめて「アプセトネデブ」ってな感じだったな。呪文みたいに唱えさせられたぜ・・


アプ・・ アプセ・・ アプセトネデブ・・ アプセトネデビ・・ あれ?