GoogleColabで自作モジュールを使う&LEET文字列

f:id:monozukuri-bu:20190724214759j:plain

ふっちーです。
今回もPyhonチュートリアルの中で、手間取った部分を書いていきます。

www.oreilly.co.jp

環境

今回もGoogle Colabolatory(Colab)を使用しました。
colab.research.google.com

準備

今回は標準・自作モジュールを使ってみるです。

Colabで自作モジュールを扱うため、ファイルをアップロードします。
今回は以下のコードを用意し、「module2019.py」という名前で保存しました。

# coding: utf-8  
def exchange(word):  
    m = {'A':'4',  'I':'1', 'E':'3', 'T':'7', 'R':'I2', 'M':'AA', 'S':'5' }  
    t = ''.join(m.get(c, c) for c in word)  
    return t  

Colabのサイドメニューからファイルタブを開き、アップロードすれば良いので非常に簡単です。

次にフォルダを作ります。

!mkdir HERO  

これで準備はOKです。

実践

ファイルを複数作る

最初に2つのリストを使ってテキストファイル(.txt)を10個作ります。

#ファイル名
title = [destiny, destroy, death, elemental, masked, vision, extra, contrast, entertainment, evil]  
#本文
main = [DESTINY, DESTROY, DEATH, ELEMENTAL, MASKED, VISION, EXTRA, CONTRAST, ENTERTAINMENT, EVIL]  

for t, m in zip(title, main):  
    with open(f'./HERO/{t}.txt','w') as f:  
        f.write(m)  

writeメソッドはファイルの中に引数の内容をファイルに書きこんでくれます。
さらに、指定したファイル名のファイルが存在しない場合は新しくファイルを作ってくれます。

ファイルを覗く

中身がちゃんと入っているかの確認はしておきたいですね。
でも、1個1個確認するのも面倒ですので、例として先頭が「d」のファイルを確認してみましょう。

今回はglobモジュールを使って確認します。
globは引数にファイルのパスを入力するとそこにあるファイルの名前をリストで返してくれます。
また、ワイルドカード等の特殊文字を使うこともできます。

import glob
l = glob.glob('HERO/[d]*.txt')  
print(l)  
出力結果:  
['HERO/death.txt', 'HERO/destiny.txt', 'HERO/destroy.txt']

頭文字dのファイルを取る事が出来ましたね。
このリストを利用して中身を覗いてみましょう。

for file_name in l:
  with open(file_name) as f:
    s = f.read()
    print(f'{file_name}: {s}')
出力結果:  
HERO/death.txt:DEATH
HERO/destiny.txt:DESTINY
HERO/destroy.txt:DESTROY

それぞれのファイルに対応した本文が書かれていますね。

Leet文字列に書き換え

Leet文字列とは、アルファベットをそれに似ている数字や記号に置き換えたものです。
例:「A」→「4」、「I]→「1」、「R」→「I2」

今回は以下の法則のみを適用します。

変換前 変換後
A 4
E 3
I 1
M AA
R I2
S 5
T 7

ここで、最初に作ったモジュールの出番です。
モジュール名.メソッド名で呼び出すことができます。

ファイルの中身をモジュールのexchangeメソッドに入れていきましょう。

import module2019

Leet_list=[]  
g = glob.glob('HERO/*.txt')  
for file_name in g:  
  with open(file_name) as f:  
    s = f.read()  
    m = module2019.exchange(s)  
    Leet_list.append(m)  
  
print(Leet_list)  
出力結果:  
['D347H', '3N73I2741NAA3N7', 'D3571NY', 'AA45K3D', '3V1L', 'V151ON', 'CON7I2457', '3L3AA3N74L', 'D357I2OY', '3X7I24']  

Leet文字列に置き換わっていますね。

モジュールの中身解説

module2019.pyの中身について解説します。

def exchange(word):
    m = {'A':'4',  'I':'1', 'E':'3', 'T':'7', 'R':'I2', 'M':'AA', 'S':'5' }
    t = ''.join(m.get(c, c) for c in word)
    return t

変数mに今回のLeet文字列に変換する法則が辞書型で格納されています。
ここで.join()の中にあるリスト内包を注目してみましょう。
変数wordに単語を入れて、1文字ずつcに渡しています。

getメソッドは第一引数のcにmと同じキーがあった場合そのバリューを返します。
つまり、cに'A'が入れば'4'を返すという事です。
同じキーが無かった場合は第二引数をそのまま返します。
今回は変換対象ではない文字をそのままcとして返しています。

あとはjoinメソッドが''で1文字ずつ結合して、tに格納してそれを返しています。

'EVIL'で例えると
1. wordに'EVIL'が格納
2. 'E'がcに入り、getメソッドが適用→'3'に変換
3. 'V'はそのまま、'I'が'1'に、'L'もそのまま
4. joinで文字同士を''で結合→'3'+''+'V'+''+'1'+''+'L'のようなイメージで結合
5. tに'3V1L'が入る

まとめ

今回は以下の内容を学習しました。 1. 自作モジュールの使用方法 2. globの使用方法

globの特殊文字は使いこなせると便利そうでした。
もっと深堀りして習得していきたいです。

Javaでbreak文/continue文/ラベル

f:id:monozukuri-bu:20190721175346j:plain

みちです。
Javaのbreak文、continue文、ラベルについて勉強していきたいと思います。

break文

break文は、繰り返し処理を中断し抜け出すときに使用されます。
繰り返し処理を抜けた後は、その後の実行文に制御が移ります。

    class Main{
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                if (i == 5) {
                    break;
                }
                System.out.println(i);
            }
            System.out.println("抜けた後の処理");
        }
    }

結果

0
1
2
3
4
抜けた後の処理

if文でi=5のときにbreak文forによって、文の繰り返し処理を中断しfor文を抜けた後の処理が実行されていることがわかりました。

continue文

continue文は、繰り返し処理内の残っている処理を飛ばして条件式に制御を移します。
そのあと繰り返し処理を続けるときに使われます。

       class Main{
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                if (i == 5) {
                    System.out.println("処理を飛ばします");
                    continue;
                }
                System.out.println(i);
            }
        }
    }

結果

0
1
2
3
4
処理を飛ばします
6
7
8
9

i=5のときにif文の中に入りcontinue文が実行されるため、5が出力されずに条件式に制御が移され、残りの繰り返し処理を行います。

ラベル

繰り返し処理文が二重になっている場合に、内側の繰り返し処理文内でbreak文、continue文を実行しても内側の繰り返し処理文しか抜けたり、飛ばしたりすることが出来ません。

そこで、ループにラベルをつけることで、ネストの深いループも一度に抜けることができます。

break文で2重ループを一気に抜ける

        class Test { 
            public static void main(String[] args) {
            loopBreak:  
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    System.out.println("i =" + i + " " + "j =" + j);
                    if (i==1 && j==1) {
                        break loopBreak;
                    }
                }
            }
            }
        }

結果

i =0 j =0
i =0 j =1
i =0 j =2
i =1 j =0
i =1 j =1

i =1, j =1のときにbreak文が実行され、それ以降の処理が行われていないことがわかります。
2重ループを一気に抜けることができました。

continue文で2重ループの処理を飛ばす

         class Test { 
            public static void main(String[] args) {
            loopContinue:  
                for (int i = 0; i < 3; i++) {
                    for (int j = 0; j < 3; j++) {
                        System.out.println("i =" + i + " " + "j =" + j);
                        if (i==1 && j==1) {
                            continue loopContinue;
                        }
                    }
                }
            }
        }

結果

i =0 j =0
i =0 j =1
i =0 j =2
i =1 j =0
i =1 j =1
i =2 j =0
i =2 j =1
i =2 j =2

i=1,j=1の時にラベルの付いたcontinue文によって、外側のfor文に制御が移ります。
結果を見るとi=1,j=1の後のi=1,j=2が実行されずに飛ばされたことがわかります。

ループ以外でラベルを使う

なお、ラベルはブロックから抜ける動作のため、ループでなくてもブロックであれば成り立ちます。

class Test {
    public static void main(String[] args) {
        int i = 1;
        int j = 1;
        label: {
            if (i == 1) {
                System.out.println("i = " + i);
                if (j == 1) {
                    break label;
                }
                System.out.println("a");
            }
        }
        System.out.println("break後");
    }
}

結果

i = 0
break後

ラベルなしの場合は内側のif文を抜けるだけなのでaも出力されるはずですが、ラベルを使うことで内側と外側のif文を抜けて処理が終了されています。

まとめ

今回はbreak文、continue文、ラベルついて勉強しました。

Javaで正規表現

f:id:monozukuri-bu:20190716225407j:plain

初めましてshadowです。
扱うたびになんだったっけ?と忘れてしまう正規表現を備忘録としてまとめてみました。

文字
説明
. 任意の1文字
\w 英単語を構成する文字(a∼z,A∼Z,1∼9)
\W 英単語を構成する文字以外
\s 空白文字(半角スペース、タブ、改行、キャリッジターン)
\S 空白以外
\d 半角数字(0∼9)
\D 半角数字以外
\b 単語の境界に一致
[abc] 指定された文字のどれかに一致(この場合abcのいずれかに)
[a-z] マッチする文字の範囲を指定する(この場合aからzの範囲)
(pattern1|pattern2) 指定されたパターンのいずれかにマッチ
* 0回以上の繰返しにマッチ
+ 1回以上の繰返しにマッチ
{n} n回の繰返しにマッチ
{n,} n回以上の繰返しにマッチ
{n,m} n回以上m回以下の繰返しにマッチ
? 0回または1回にマッチ
(pattern)+ patternの1回以上の繰返しにマッチ
^pattern 文字列の先頭にpetternがある文字列にマッチ
pattern$ 文字列の末尾にpatternがある文字列にマッチ

実践

実際に使うことがありそうな正規表現をまとめてみました。

郵便番号

ハイフン有り

^\d{3}-\d{4}$

ハイフン無し

^\d{7}$

電話番号

固定電話

固定電話の場合は先頭が0で始まります。
その後に市外局番1∼4桁、市内局番1∼4桁を組み合わせた5桁の数字、そして4桁の加入者番号が続き、全部で10桁になります。

^0\d-\d{4}-\d{4}$
^0\d{2}-\d{3}-\d{4}$
^0\d{3}-\d{2}-\d{4}$
^0\d{4}-\d-\d{4}$

携帯電話

070、080、090の3パターン先頭に4桁、4桁と続きます。

^0[789]0-\d{4}-\d{4}$

IP電話

先頭が050で始まり、4桁、4桁と続きます。

^050-\d{4}-\d{4}$

フリーダイヤル

先頭0120の3桁、3桁です。

^0120-\d{3}-\d{3}$

カード番号

全てを入れると多いのでよく見るものだけ書いています。

VISA

4から始まる13桁か16桁の番号です。

^4\d{12}(\d{3})?$

MasterCard

51∼55で始まる16桁の番号です。

^5[1-5]\d{14}$

Discover Card

6011から始まる16桁の番号です。

^6011\d{12}$

American Express

34か37で始まる15桁の番号です。

^3[47]\d{13}$

JCB

2131か1800で始まる15桁、あるいは35で始まる16桁の番号です。

^(2131|1800|35\d{3})\d{11}$

パスワード

半角英子文字、大文字、数字を各1文字ずつ含み、8文字以上100文字未満の文字列です。

^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)[a-zA-Z\d]{8,100}$

動作確認

用意した文字列達はこちらです。
1つずつマッチするものが入っています。

"457-9854"
"4561-9852"
"4569854"
"65976566646"
"0120-572-645"
"06-2485-5875"
"4545-68554-54"
"072-654-3213"
"090-6548-8452"
"0754-89-7845"
"8-8-8"
"050-2845-9754"
"04745-8-8541"
"4568215795632410"
"6011456210587101"
"1234585412005698" 
"346569321047475"
"180069397954858"
"12345678912346578"
"5564872101047896"
"passwd"
"p@sswd1234"
"Passwd1234"

実行結果は以下のようになりました。

457-9854 は ^\d{3}-\d{4}$ にマッチします
4569854 は ^\d{7}$ にマッチします
06-2485-5875 は ^0\d-\d{4}-\d{4}$ にマッチします
072-654-3213 は ^0\d{2}-\d{3}-\d{4}$ にマッチします
0754-89-7845 は ^0\d{3}-\d{2}-\d{4}$ にマッチします
04745-8-8541 は ^0\d{4}-\d-\d{4}$ にマッチします
090-6548-8452 は ^0[789]0-\d{4}-\d{4}$ にマッチします
050-2845-9754 は ^050-\d{4}-\d{4}$ にマッチします
0120-572-645 は ^0120-\d{3}-\d{3}$ にマッチします
4568215795632410 は ^4\d{12}(\d{3})?$ にマッチします
5564872101047896 は ^5[1-5]\d{14}$ にマッチします
6011456210587101 は ^6011\d{12}$ にマッチします
346569321047475 は ^3[47]\d{13}$ にマッチします
180069397954858 は ^(2131|1800|35\d{3})\d{11}$ にマッチします
Passwd1234 は ^(?=.*?[a-z])(?=.*?[A-Z])(?=.*?\d)[a-zA-Z\d]{8,100}$ にマッチします

まとめ

Java正規表現を使って複数の候補からマッチする文字列を検索しました。
色々なパターンがあるので使いこなしていきたいです。

PythonでJSONを弄りたい

f:id:monozukuri-bu:20190712140150j:plain

ふっちーです。よろしくお願いします。
現在python3エンジニア認定基礎試験の資格を取得するため、日々勉強に励んでいます。
資格の参考書として使用しているPythonチュートリアルの中で、手間取ったところ書いてみます。

www.oreilly.co.jp

環境

今回はGoogle Colabolatory(Colab)を使用しました。
colab.research.google.com

実践

準備

まず、作業ディレクトリを作ります。
Colabではコマンドの先頭に!をつけることでLinuxコマンドを実行できます。

!mkdir work

辞書型データの作成

まず、Pythonで辞書型(Dict)のデータを作ります。
2種類の方法で作ってみました。

①数値をKeyにする

HutoRider = {}  
memory = ['cyclone', 'joker', 'heat', 'metal', 'luna', 'trigger', 'fang', 'xtreme', 'prizm', 'accel', 'engine', 'trial']
for index, value in enumerate(memory):  
        HutoRider[index] = value  
print(HutoRider)  
出力結果:  
{0: 'cyclone', 1: 'joker', 2: 'heat', 3: 'metal', 4: 'luna', 5: 'trigger', 6: 'fang', 7: 'xtreme', 8: 'prizm', 9: 'accel', 10: 'engine', 11: 'trial'}  

enumerate関数を使って、リストの先頭から順にインデックスを振りました。
enumerate関数は引数の要素と、そのインデックス値を同時に返してくれます。

②文字(列)をKeyにする

T2memories = {}  
AtoZ = [chr(i) for i in range(ord('A'), ord('Z')+1)] #ordでascIIコード化  
print(AtoZ)  
AtoZvalue = ['アクセル','バード', 'サイクロン', 'ダミー', 'エターナル', 'ファング', 'ジーン', 'ヒート', 'アイスエイジ', 'ジョーカー', 'キー', 'ルナ', 'メタル', 'ナスカ', 'オーシャン', 'パペティア', 'クイーン', 'ロケット', 'スカル', 'トリガー', 'ユニコーン', 'バイオレンス', 'ウェザー', 'エクストリーム', 'イエスタディ', 'ゾーン']  
for alphabet, value in zip(AtoZ, AtoZvalue):  
        T2memories[alphabet] = value  
print(T2memories)  
出力結果:
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z']

{'A': 'アクセル', 'B': 'バード', 'C': 'サイクロン', 'D': 'ダミー', 'E': 'エターナル', 'F': 'ファング', 'G': 'ジーン', 'H': 'ヒート', 'I': 'アイスエイジ', 'J': 'ジョーカー', 'K': 'キー', 'L': 'ルナ', 'M': 'メタル', 'N': 'ナスカ', 'O': 'オーシャン', 'P': 'パペティア', 'Q': 'クイーン', 'R': 'ロケット', 'S': 'スカル', 'T': 'トリガー', 'U': 'ユニコーン', 'V': 'バイオレンス', 'W': 'ウェザー', 'X': 'エクストリーム', 'Y': 'イエスタディ', 'Z': 'ゾーン'}  

1つ目の出力結果はAtoZ、2つ目の出力結果はT2memoriesです。

AtoZはリスト内包でAからZのリストを作成しています。
以下でも同じ結果が得られます。

AtoZ=[]  
for i in range(ord('A'), ord('Z')+1):  
  AtoZ.append(chr(i))  

こちらはzip関数を使って、2つのリストの先頭から一つずつ取り出してKeyとValueにしています。
zip関数でまとめるリストは同じ長さである必要があります。

JSONファイルの作成

先ほど作った辞書型のデータを使ってjsonファイルを作ってみます。

辞書型のデータをJSON形式に変換するには、jsonモジュールのdumpメソッドを使います。
このJSONデータをファイルに書き込みたいので、書き込みモード(w)でopenしておきます。

import json  

with open('./work/RiderMemory.json', 'w') as f:  
  json.dump(HutoRider, f)  

with open('./work/T2Memories.json', 'w') as f:
  json.dump(T2memories, f)

これでJSONファイルが2つ出来ました。結構あっさりですね。

中身の確認

きちんとファイルに書き込めているでしょうか。
ファイルを開いて確認してみます。

f = open('./work/RiderMemory.json','r')  

Riderdir = json.load(f)  
print(Riderdir)  
出力結果:
{'0': 'cyclone', '1': 'joker', '2': 'heat', '3': 'metal', '4': 'luna', '5': 'trigger', '6': 'fang', '7': 'xtreme', '8': 'prizm', '9': 'accel', '10': 'engine', '11': 'trial'}  

大丈夫そうで・・・あれ?
ファイルに書き込む前の辞書型データをもう一度見てみましょう。

たしか、こんな感じでした。

{0: 'cyclone', 1: 'joker', 2: 'heat', 3: 'metal', 4: 'luna', 5: 'trigger', 6: 'fang', 7: 'xtreme', 8: 'prizm', 9: 'accel', 10: 'engine', 11: 'trial'}    

JSON形式に変換したデータは、キーの数字がシングルクォートで囲まれています!

これは、Pythonではキーに文字列以外の値(今回は数値)を使えますが、JSONでは文字列に限定されているためです。
辞書型のデータのKeyに文字列以外が入っていると、JSONに変換した際に文字列に変換されます。

もう少し見やすくしたい

ところで、普通に出力すると見にくいですね・・・。
折角なので、もう少し見やすくしてみましょう。

RiderStr = json.dumps(Riderdir, indent = 4)  
print(RiderStr)   
出力結果:  
{  
    "0": "cyclone",  
    "1": "joker",  
    "2": "heat",  
    "3": "metal",  
    "4": "luna",  
    "5": "trigger",  
    "6": "fang",  
    "7": "xtreme",  
    "8": "prizm",  
    "9": "accel",  
    "10": "engine",  
    "11": "trial"  
}  

Key:Valueの単位で改行が入り、見やすくなりましたね。
json.dumps()で文字列化した際にindent=4とすることで、空白スペース4つ分のインデントが入りました。

文字化けするとき

json.dumps()で文字化けしてしまう場合、dumpsメソッドの引数にensure_ascii=Falseを追加するとうまく動いてくれます。

T2Str = json.dumps(T2dir, ensure_ascii=False, indent = 4)
print(T2Str)
出力結果:  
{  
    "A": "アクセル",  
    "B": "バード",  
    "C": "サイクロン",  
    "D": "ダミー",  
    "E": "エターナル",  
    "F": "ファング",  
    "G": "ジーン",  
    "H": "ヒート",  
    "I": "アイスエイジ",  
    "J": "ジョーカー",  
    "K": "キー",  
    "L": "ルナ",  
    "M": "メタル",  
    "N": "ナスカ",  
    "O": "オーシャン",  
    "P": "パペティア",  
    "Q": "クイーン",  
    "R": "ロケット",  
    "S": "スカル",  
    "T": "トリガー",  
    "U": "ユニコーン",  
    "V": "バイオレンス",  
    "W": "ウェザー",  
    "X": "エクストリーム",  
    "Y": "イエスタディ",  
    "Z": "ゾーン"  
}  

ソートする

辞書型のデータのKeyの順番を変えます。
今までの辞書型はKeyを軸に並んでいたので、Valueを軸に並び変えてみます。

RiderSort = sorted(Riderdir.items(), key=lambda x: x[1])
print(RiderSort)
出力結果:  
[('9', 'accel'), ('0', 'cyclone'), ('10', 'engine'), ('6', 'fang'), ('2', 'heat'), ('1', 'joker'), ('4', 'luna'), ('3', 'metal'), ('8', 'prizm'), ('11', 'trial'), ('5', 'trigger'), ('7', 'xtreme')]  

Valueがアルファベット順に並びました!

今回のキーはlambda式ですね。(Keyだけに)
lambda式は無名関数と呼ばれ、lambda 引数: 処理と書きます。

まとめ

今回はJSONの扱いを通して、以下の内容を学びました。
・enumerate
・zipメソッド
・リスト内包
・lambda式

Vimコマンド備忘録

f:id:monozukuri-bu:20190708223717j:plain

VimとはLinuxを含むUnixベースのOSで利用できるテキストエディタで、Linuxにはプリインストールされています。

viコマンドで起動します。

vi ファイル名  

ファイル名を指定しない場合は、保存する際にファイル名を入力します。
また、-Rを指定することで、読み取り専用として開くことが可能です。

$ vi -R ファイル名

Vimには2つのモードがあります。

モード できること
コマンドモード 文字列の削除、コピー、貼り付け、Vimの終了
編集モード 文字の入力、編集

コマンドモードから編集モードへ切り替える

コマンド
説明
i カーソルの左から入力する
I 行頭から入力する
a カーソルの右から入力する
A 行末から入力する
o カーソルがある行の下に空白行を挿入してそこから入力する
O カーソルがある行の上に空白行を挿入してそこから入力する

入力モードからコマンドモードへ切り替える

コマンド
説明
Esc 入力モードからコマンドモードへ

コマンドモードでのカーソル移動

コマンド
説明
h ←キーと同じ、カーソルを1文字左へ
l →キーと同じ、カーソルを1文字右へ
k ↑キーと同じ、カーソルを1文字上へ
j ↓キーと同じ、カーソルを1文字下へ
0 行頭へ移動
$ 行末へ移動
H 画面の一番上の行頭へ移動
L 画面の一番下の行末へ移動
gg ファイルの先頭行へ移動
G ファイルの最終行へ移動
nG ファイルのn行目へ移動(:nでも同じ)

コマンドモードでの編集

コマンド
説明
x deleteキーと同じ、カーソル位置の文字を削除
X Backspaceキーと同じ、カーソル位置の前の文字を削除
dd 現在のカーソル位置の行を削除
dw カーソル位置から次の単語の先頭まで削除
yy カレント行のコピー
p カーソル位置の後ろ(右側)にコピーしたものを張り付け
P カーソル位置の前(左側)にコピーしたものを張り付け
r カーソル位置の1文字を置換
:s/old/new oldをnewに置換する

コマンドモードでの検索

コマンド
説明
/aaa カーソル位置からファイル末尾に向かって、指定した文字列(aaa)の検索
?aaa カーソル位置からファイル先頭に向かって、指定した文字列(aaa)の検索
n 上記の検索の次の文字列の検索
N 上記の検索の次の検索(逆方向)

Vimの終了

コマンド
説明
:q 編集データを保存せずに終了(保存するか確認がある)
:q! 編集データを保存せずに強制終了
:w 編集データを上書き保存
:w! 編集データを強制的に上書き保存
:wq 編集データを保存して終了
:wq! 編集データを強制的に上書き保存して終了
:n,m w n行目からm行目までを保存
ZZ 編集データを保存して終了(:wqと同じ)
:e! 最後に保存した内容に復帰
:rファイル名 指定したファイル名の内容を現在の行以降に読み込み
:!コマンド viを終了せずにコマンドを実行
:r! コマンドの実行結果を挿入

まとめ

今回はVimの扱い方について勉強しました。
しっかりとコマンドを覚えて効率的にファイル操作できるように頑張ります。

Djangoでのデータベースの扱い方

f:id:monozukuri-bu:20190708221030j:plain

Oracle Master 12c Bronze SQL基礎の資格取得に向け日々精進しているhashiです。

今回は自分が過去に扱ったDjango(ジャンゴ)というPythonのWebアプリケーションフレームワークでのデータベース操作を行う主なクエリと、それと等価なSQL文を併記して、知識を整理することを目的とします。

動作環境

Python 3.7.3
Django 2.2.1

テーブルの生成

Djangoでのテーブル定義は以下のように記述します。
クラス名がテーブル名、クラス変数の左辺が列の名前、右辺に型の情報やオプションを記述しています。

"""models.py"""
from django.db import models


class UserTable(models.Model):
    """ユーザー情報のテーブル"""
    user_id = models.IntegerField("ID", primary_key=True)
    name = models.CharField("ユーザー名", max_length=30)
    age = models.IntegerField("年齢", blank=True, null=True)


class PhoneTable(models.Model):
    """ユーザーの持つ電話のテーブル"""
    user = models.ForeignKey(UserTable, on_delete=models.CASCADE)
    tel = models.CharField("電話番号", max_length=15)
    email = models.EmailField("メールアドレス")
    contract_date = models.DateField("契約日", blank=True, null=True)

上のように記述した後、「データベースマイグレーション」という操作を行うとデータベース上にテーブルが作られます。

vsCode拡張機能であるSQLiteを用いてデータベースの情報を確認すると……

UserTable

NULLを許容する 制約
user_id integer NOT NULL PRIMARY KEY
name varchar(30) NOT NULL
age integer

PhoneTable

NULLを許容する 制約
user integer NOT NULL REFERENCES
tel varchar(15) NOT NULL
email varchar(254) NOT NULL
contract_date date

このように、ユーザー情報と携帯電話の情報という2つのテーブルが作られます。

テーブルからのオブジェクトの取得

Djangoでは、データベースからオブジェクトを取得する際に「クエリセット」を作ります。
Djangoのドキュメント(https://docs.djangoproject.com/ja/2.2/topics/db/queries/)によれば、

データベースからオブジェクトを取得するには、モデルクラスの Manager から QuerySet を作ります。 QuerySet はデータベース上のオブジェクトの集合を表しています。多数の フィルター を持つことができます。フィルターは与えられたパラメータに基づいてクエリの検索結果を絞り込みます。SQL 文においては、 QuerySet は SELECT 句、フィルターは WHERE や LIMIT のような絞り込みに用いる句に対応しています。

とあります。実際に絞り込みを行ってみます。
先ほど作ったテーブルに以下のようなデータを追加しました。
これからの操作はこのデータを用いて行います。

UserTable

user_id name age
1 tanaka 34
2 sato
3 suzuki 40
4 sasaki 24

PhoneTable

user TEL email contract_date
1 0120-456-456 tanaka@t.com 2018年10月1日
1 090-1234-5678 tanaka2@t.com 2019年8月1日
2 0120-999-999 test@t.com 2019年7月4日
3 0120-202-102 temp@temp.com 2018年11月7日
2 0120-68-1147 phone@t.com 2019年7月23日
1 0120-976-543 testtest@t.com
1 0120-456-456 karikari@test.com
4 456 poi@poi.jp

全データの取得

phones0 = PhoneTable.objects.all() #全データの取得

PhoneTableクラスのManagerであるobjectsにall() メソッドを呼ぶことで全データを取得できます。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable;

取得したデータ

user TEL email contract_date
1 0120-456-456 tanaka@t.com 2018年10月1日
1 090-1234-5678 tanaka2@t.com 2019年8月1日
2 0120-999-999 test@t.com 2019年7月4日
3 0120-202-102 temp@temp.com 2018年11月7日
2 0120-68-1147 phone@t.com 2019年7月23日
1 0120-976-543 testtest@t.com
1 0120-456-456 karikari@test.com
4 456 poi@poi.jp

完全一致検索

取得するオブジェクトの絞り込みを行う際はfilter()メソッドを利用します。引数に入れる条件を変えることで検索条件を変えることができ、

phones1 = PhoneTable.objects.filter(tel='0120-456-456')  # 完全一致検索

列名=値を引数にすることでPhoneTableのtel列が0120-456-456に等しいデータのみを取得できます。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE tel='0120-456-456';

取得したデータ

user TEL email contract_date
1 0120-456-456 tanaka@t.com 2018年10月1日
1 0120-456-456 karikari@test.com

部分一致検索

完全一致だけでなく部分一致も可能です。

phones2 = PhoneTable.objects.filter(tel__contains='456')  # 部分一致検索

列名__contains=値を引数にすることでPhoneTableのtel列に456を含むデータのみを取得できます。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE tel LIKE '%456%';

取得したデータ

user TEL email contract_date
1 0120-456-456 tanaka@t.com 2018年10月1日
1 0120-456-456 karikari@test.com
4 456 poi@poi.jp

リスト中からの検索

複数の値のどれかと等しいデータの取得が可能です。

# リスト中からの検索
phones3 = PhoneTable.objects.filter(tel__in=['0120-456-456', '0120-999-999'])

列名__in=[値1,値2…]を引数にすることでPhoneTableのtel列がリストのどれかと等しいデータを取得できます。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE tel IN ('0120-456-456', '0120-999-999');

取得したデータ

user TEL email contract_date
1 0120-456-456 tanaka@t.com 2018年10月1日
2 0120-999-999 test@t.com 2019年7月4日
1 0120-456-456 karikari@test.com

○○未満のものを検索

# 比較対象未満(日付なら過去)を検索
phones4 = PhoneTable.objects.filter(contract_date__lt=datetime.date.today())

列名__lt=値を引数にすることでPhoneTableのcontract_date列がdatetime.date.today()(=2019年7月5日)より前のデータのみを取得できます。(NULLとの比較はできない)
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE contract_date < SYSDATE;

取得したデータ

user TEL email contract_date
1 0120-456-456 tanaka@t.com 2018年10月1日
2 0120-999-999 test@t.com 2019年7月4日
3 0120-202-102 temp@temp.com 2018年11月7日

○○より大きいものを検索

# 比較対象より大きいもの(日付なら未来)を検索
phones5 = PhoneTable.objects.filter(contract_date__gt=datetime.date.today())

列名__gt=値を引数にすることでPhoneTableのcontract_date列がdatetime.date.today()(=2019年7月5日)より後のデータのみを取得できます。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE contract_date > SYSDATE;

取得したデータ

user TEL email contract_date
1 090-1234-5678 tanaka2@t.com 2019年8月1日
2 0120-68-1147 phone@t.com 2019年7月23日

NULL判定

# NULLの判定
phones6 = PhoneTable.objects.filter(contract_date__isnull=True)

列名__isnull=Trueを引数にすることでPhoneTableのcontract_date列がNULLのデータのみを取得できます。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE contract_date IS NULL;

取得したデータ

user TEL email contract_date
1 0120-976-543 testtest@t.com
1 0120-456-456 karikari@test.com
4 456 poi@poi.jp

外部キーで検索

# 外部キーで検索
temp = UserTable.objects.get(age=34)
phones7 = PhoneTable.objects.filter(user=temp)

外部キーを条件とした検索も可能です。上では2段階に分けて検索を行っています。
1行目でget()を使用している理由ですが、filter()では検索結果が1つの場合でも返り値の型がリストになってしまうためです。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE (SELECT user_id FROM UserTable WHERE age = 34) = 1;

取得したデータ

user TEL email contract_date
1 0120-456-456 tanaka@t.com 2018年10月1日
1 090-1234-5678 tanaka2@t.com 2019年8月1日
1 0120-976-543 testtest@t.com
1 0120-456-456 karikari@test.com

and検索

# and検索
phones8 = PhoneTable.objects.filter(tel="0120-999-999", email="test@t.com")

filter()の引数に複数の検索条件をカンマで区切って入れることで、そのすべてに当てはまるデータを取得できます。
SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE tel='0120-999-999' AND email='test@t.com';

取得したデータ

user TEL email contract_date
2 0120-999-999 test@t.com 2019年7月4日

or検索

from django.db.models import Q
# or検索
phones9 = PhoneTable.objects.filter(Q(tel="0120-999-999") | Q(email="phone@t.com"))

or検索は少し特殊で、Qオブジェクトというものを使って実現しています。Q(条件1) | Q(条件2)という形式になります。 SQL文で表すとこのようになるでしょう。

SELECT * FROM PhoneTable WHERE tel='0120-999-999' OR email='phone@t.com';

取得したデータ

user TEL email contract_date
2 0120-999-999 test@t.com 2019年7月4日
2 0120-68-1147 phone@t.com 2019年7月23日

まとめ

Djangoにおける主要なデータベースからのデータ取得方法と、それに対応するSQL文についてまとめました。
今後はINSERTやUPDATE関連もまとめたいと思います。

Java基礎 演算子について

f:id:monozukuri-bu:20190708215355j:plain

初めまして、みちです。 Javaの単行演算子について勉強していきたいと思います。

演算子の優先度

演算子には、結合法則と優先度があります。
結合法則とは、同じ優先度が存在した場合、左から計算するか(左結合)、または右から計算するか(右結合)を表しています。
そして優先度とは、1行の式の中に複数の演算子があった場合に、どの演算子から計算をしていくのかという順番のことです。
優先度は、以下の表で上から優先度が高く、下に行くほど低くなります。

種類 演算子 結合法則
単項演算子 ++ -- + - ~ ! (キャスト) 右結合
算術演算子 * / % 左結合
+ - 左結合
シフト演算子 << >> >>>    左結合
ビット演算子 & 左結合
^ 左結合
| 左結合
論理演算子  && 左結合
  || 左結合
3項演算子  ? 右結合
代入演算子  = *= /= %= += -= <<= >>= >>>= &= ^= |= 右結合

単項演算子

単項演算子とは、一つの変数に対して使われている演算子のことです。
インクリメント(1を加算)、デクリメント(1を減算)があり、変数の前後の配置によって動きが変わってきます。

・変数の後ろに置いた場合(後置)

int i = 1;
int j = i++;

後置の場合は、iの変数をjに先に代入してからiに1を加算します。結果としては以下の表の通りです。

iの初期値 実行後のiの値 実行後のjの値
1 j = i++; 2 1

・変数の前に置いた場合(前置)

int i = 1;
int j = ++j;

前置の場合は、iの変数に1を加算してからjの変数に加算します。結果としては以下の表の通りです。

iの初期値 実行後のiの値 実行後のjの値
1 j = ++i; 2 2

問題

自分があまり理解していない時に悩んでいた問題を出したいと思います。

class Test {
    public static void main(String[] args) {
        int x = 10;
        int y = x++;
        int z = ++x;
        System.out.println("x = " + x);
        System.out.println("y = " + y);
        System.out.println("z = " + z);
    }
}




答え

x = 12
y = 10
z = 12

yは、xの値を代入します。そのあとにxに1を加算します。
zは、xに1加算してから代入します。
前置と後置の動きの違いを理解したらわかると思います。

代入演算子と複合代入演算子

代入演算子とは、右辺の値を、左辺の変数に代入するときに使う演算子です。
複合代入演算子は、「+」や「-」などの演算子を代入演算子と組み合わせた演算子です。

演算子 記述例 算術演算子の記述
= a = b -
+= a += b a = a + b
-= a -= b a = a - b
*= a *= b a = a * b
/= a /= b a = a / b
%= a %= b a = a % b

class Test{
    public static void main(String[] args) {
        int a = 10; int b = 2;
        System.out.println(a += b); //a = 10 + 2;
        System.out.println(a -= b); //a = 12 - 2;
        System.out.println(a *= b); //a = 10 * 2;
        System.out.println(a /= b); //a = 20 / 2;
        System.out.println(a %= b); //a = 10 % 2;
    }
}

結果

12
10
20
10
0

関係演算子

関係演算子は、2つの値を比較して、その結果をtrueかfalseで返します。
trueの場合は、2つの値が一致していることを表しています。
falseの場合は、2つの値が不一致であることを表しています。

演算子 記述例 説明
== a == b aとbの値が等しければtrue、異なればfalse
!= a != b aとbの値が異なればtrue、等しければfalse
> a > b aの値がbの値よりも大きければtrue、以下であればfalse
>= a >= b aの値がb以上であればtrue、小さければfalse
< a < b aの値がbより小さければtrue、以上であればfalse
<= a <= b aの値がb以下であればtrue、大きければfalse

class Test {
    public static void main(String[] args) {
        int a = 1; int b = 2; int c = 1;
        System.out.println(a == b);
        System.out.println(a == c);
        System.out.println(a != b);
        System.out.println(a != c);
        System.out.println(a < b);
        System.out.println(a <= c);
        System.out.println(a > b);
        System.out.println(a >= c);
    }
}

結果

false
true
true
false
true
true
false
true

まとめ

今回は演算子について勉強しました。