talosのプログラミング教室

Java Silver合格への道 ~アンダースコアを用いた数値表記~

こんにちは。たろすです。

今回はアンダースコアを用いた数値表記についてです。

使い方

Java SE 7以降では数値リテラルにアンダースコアを使うことができます。

int x = 123_456;

使える条件

リテラルの先頭と末尾ではないこと
・記号の前後ではないこと

つまり以下のような場合はコンパイルエラーになります。

int y = _123456;
int z = 123456_;
double a = 1._23;
float b = 1.23_f;

おわりに

今回はアンダースコアを用いた数値表記について説明しました。

「,」の代わりに使用すると可読性が上がるかもしれませんね。

Java Silver合格への道 ~可変長引数~

こんにちは。たろすです。

今回は可変長引数についてです。

可変長引数の使い方

public class Sample {

	public void method(int... values) {
		for (int value : values) {
			System.out.println(value);
		}
		System.out.println("------------");
	}
}

メソッドの引数に注目してください。

「int... values」と書かれています。

引数の型の直後に「...」を付けることで可変長引数にすることができます。

可変長引数として渡された引数は配列として扱われます。

このメソッドを実際に使ってみましょう。

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ

		Sample sample = new Sample();
		sample.method(1, 2, 3);
		sample.method(1, 2, 3, 4, 5);
	}
}
// 出力
1
2
3
------------
1
2
3
4
5
------------

引数に好きなだけ値を渡すことができます。

制約

可変長引数以外の引数も受け取る場合は、可変長引数が最後の引数になるようにしなければなりません。

例えば以下のようなプログラムはコンパイルエラーになります。

public void method(int... values, String str) { }

以下のように直しましょう。

public void method(String str, int... values) { }

可変長引数を二つ以上受け取るのもNGです。

おわりに

今回は可変長引数について説明しました。

配列を渡せば済む話なので、あまり使われているところは見たことありません。

Java Silver合格への道 ~ラベル~

こんにちは。たろすです。

今回はラベルについてです。

ラベルの使い方

まず以下のコードを見てください。

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ

		for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				System.out.println(i + " " + j);
				if (j == 3) {
					break;
				}
			}
		}
	}
}

二重ループがあり、内側のループの中でjが3と等しければbreakするような構造になっています。

そのためこのプログラムを実行すると、

0 0
0 1
0 2
0 3
1 0
1 1
1 2
1 3
2 0
2 1
2 2
2 3
3 0
3 1
3 2
3 3
4 0
4 1
4 2
4 3

と出力されます。

j(右側の列)が3になったろころで内側のループを抜けていることがわかります。


次に以下のコードを見てください。

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ

		a: for (int i = 0; i < 5; i++) {
			for (int j = 0; j < 5; j++) {
				System.out.println(i + " " + j);
				if (j == 3) {
					break a;
				}
			}
		}
	}
}

先程と異なるのは外側のforの左側に「a:」がついていることと、breakの右側に「a」がついていることです。

これをラベルと呼びます。

このプログラムを実行すると以下の出力が得られます。

0 0
0 1
0 2
0 3

jが3と等しくなったところで外側のループを抜けていることがわかります。

これはラベルを付けたことでbreakと外側のforを紐づけたからです。

このようにラベルを使うことでbreakするループを変えたりすることができます。

おわりに

今回はラベルについて説明しました。

goto文同様に可読性が落ちるため現場ではまず見ませんが、Java Silver勉強中は覚えておきましょう。

Java Silver合格への道 ~配列の宣言と初期化~

こんにちは。たろすです。

今回は配列の宣言と初期化についてです。

宣言

OK例

// よく使われる宣言
int[] a;  

// 変数名の後に[]を書くことも可能
int b[];  

// 多次元配列も同様
int[][] c;  
int d[][];

// 分けることもできる
int[] e[];  

NG例

// 宣言時に要素数は指定しない
int a[3];  

初期化

OK例

// 要素を指定するパターン
int[] a = { 1, 2, 3 }; 

// やっていることは上と同じ
int[] b = new int[] { 1, 2, 3 }; 

// 要素数だけを指定するパターン(要素は初期値)
int[] c = new int[3]; 

// 多次元配列の場合は{}の数に注意
int[][] d = { { 1, 2 }, { 3, 4 } }; 

// 要素なしの場合は多次元配列でも{}は1つでOK
int[][] e = {}; 

// N次元目までは要素数を決めておき、N+1次元目以降の要素数は未定にしておくことができる
int[][][]f = new int[3][][];  

NG例

// {}を使う場合は要素数を指定しない
int[] a = new int[3] { 1, 2, 3 }; 

// 宣言と{}を使った初期化は分けられない
int[] b;
b = {1 ,2, 3}; 

// 次元数が一致しない
int[][] c = { 1, 2, 3 }; 

// N次元目の要素数が決まっていないのに、N+1次元目以降の要素数を決めることはできない
int[][][] d = new int [][3][];  

おわりに

今回は配列の宣言と初期化についてOK例とNG例を挙げました。

他の言語などを触っていると間違えやすいので要注意です。


Java Silver合格への道 ~コンスタントプール~

こんにちは。たろすです。

今回はコンスタントプールについて説明します。

a == b

Javaではa == bと書くとaとbが同一かを判定し、同一であればtrue、同一でなければfalseを返します。

つまり以下のようなプログラムを実行すると「false」と表示されます。

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ

		Food a = new Food("gohan");
		Food b = new Food("cake");

		System.out.println(a == b);
	}

}

どこからどう見ても別のオブジェクトですからね。

それでは次のプログラムはどうでしょうか。

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ

		Food a = new Food("gohan");
		Food b = new Food("gohan");

		System.out.println(a == b);
	}

}

実はこの場合も「false」と表示されます。

なぜなら別々にインスタンス化しているため、別のインスタンスとして扱われるからです。

コンスタントプール

String型の場合はダブルクォーテーションで括られた文字列を記述するだけでインスタンス化できます。

つまり、わざわざnewしなくてもインスタンス化できるため以下のように書くことができます。

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ
		
		String a = "gohan";
		String b = "gohan";

		System.out.println(a == b);
	}

}

しかし、このプログラムを実行すると「true」と表示されます。

なぜこのような挙動をするかというとコンスタントプールという仕組みがあるからです。

コンスタントプールは文字列リテラルを定数値としてインスタンスとは異なるメモリに保存し、どの変数から参照されても同じ文字列の場合は同じ場所を参照することでメモリの消費を減らす仕組みです。

つまりこのプログラムでは変数aも変数bもコンスタントプール内の同じ場所を参照しているため、aとbは同一であると判定されたわけです。


一方で、newした場合は新しいインスタンスが作られるため、以下のプログラムを実行すると「false」と表示されます。

public class Main {

	public static void main(String[] args) {
		// TODO 自動生成されたメソッド・スタブ

		String a = new String("gohan");
		String b = new String("gohan");

		System.out.println(a == b);
	}

}

つまりこの書き方はメモリを無駄に消費しているので、実際にはやらないほうが良いです。

おわりに

今回はコンスタントプールについて説明しました。

String型の比較はequals()を使うのが普通なので意外とこの仕組みは知らないかもしれませんね。


【Java】パスワードのハッシュ化とログイン認証

こんにちは。たろすです。

今回は安全なアプリを開発するために重要なパスワードのハッシュ化について説明します。

ハッシュ化のアルゴリズムとしては「bcrypt」を使用します。

環境

言語:Java8
フレームワーク:Spring Security
DB:MySQL

下準備

mvnrepository.com

このサイトからjarファイルをダウンロードします。

f:id:talosta:20201227173038p:plain

Eclipseの動的Webアプリケーションであれば、ダウンロードしたjarファイルをWEB-INF/libにコピーしましょう。

解説

認証関連はAutuUtilクラスに実装されています。

まずはSpring Securityを使うために以下をインポートします。

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

パスワードのハッシュ化

/**
 * ユーザーを登録する
 * @param user
 * @return
 */
public boolean registerUser(UserSample user) {
	BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();
	// ハッシュ化
	String encodeedPassword = bcpe.encode(user.getPassword());
	user.setPassword(encodeedPassword);

	int result = userLogic.insertUser(user);
	if (result > 0) {
		return true;
	}
	return false;
}

ハッシュ化するときにはBCryptPasswordEncoder.encode()メソッドを呼びます。

引数は生のパスワード(ハッシュ化する前のパスワード)です。

ログイン認証

/**
 * ログイン処理を行う
 * @param user
 * @return
 */
public boolean login(UserSample user) {
	// IDをキーにユーザー情報を取得
	UserSample compUser = userLogic.getUserById(user.getId());
	BCryptPasswordEncoder bcpe = new BCryptPasswordEncoder();
	// 入力されたパスワードとDBのパスワード(ハッシュ化済み)を比較
	if (bcpe.matches(user.getPassword(), compUser.getPassword())) {
		loginUser.setUser(compUser);
		return true;
	}
	return false;
}

ユーザーから入力されたパスワードとDBに登録されているハッシュ化されたパスワードが一致しているかを確認するにはBCryptPasswordEncoder.matches()を使います。

第一引数は生のパスワード(ユーザーから入力されたパスワード)、第二引数はハッシュ化されたパスワード(DBに登録されたパスワード)です。


スポンサーリンク



実行

実行するとトップページが開くので「新規登録」を押します。

f:id:talosta:20201228230344p:plain

ID、名前、パスワードをすべて入力して「登録」を押します。

f:id:talosta:20201228230422p:plain

DBに登録されました。

パスワードはちゃんとハッシュ化されています。

f:id:talosta:20201228230448p:plain

先程登録したIDとパスワードを入力して「ログイン」を押します。

f:id:talosta:20201228232128p:plain

登録した名前が表示されます。

f:id:talosta:20201228232146p:plain

おわりに

今回はパスワードのハッシュ化とログイン認証について説明しました。

不明点があればコメントお願いします。

【Python】Twitter APIで一方的にフォローしている人を洗い出して自動フォロー解除する

こんにちは。talosです。

TwitterなどのSNSで一方的にフォローしているのって悲しいですよね。

だからと言って一人ひとり解除するのは面倒くさい…

なのでTwitter APIを使って一方的にフォローしている人を洗い出し、フォロー解除します。

環境

言語:Python
ライブラリ:tweepy

事前準備

Twitter APIの申請をしてある必要があります。

申請の方法はGoogleで調べればいくらでも出てくるのでここでは触れません。

方針

1.フォローしている人とフォロワーをリストアップする

2.1で作成したリストを比較してフォローしている人のリストにだけ存在する人をリストアップする

3.2で作成したリストのユーザーをフォロー解除する

フォローしている人とフォロワーをリストアップする

CK、CS、AT、ASにはそれぞれご自身で取得したAPI KEY、API secret key、Access token、Access token secretを入れてください。

また、「フォロワー取得」と「フォローしている人取得」で呼んでいるtweepy.Cursor()のidのところにご自身のアカウント名(@からはじまるやつ)を入れてください。

CSV出力」をするフォルダはご自身の環境に合わせて決めてください。

import tweepy
import pandas as pd

CK = '****'    # API KEY
CS = '****'    # API secret key
AT = '****'    # Access token
AS = '****'    # Access token secret

auth = tweepy.OAuthHandler(CK, CS)
auth.set_access_token(AT, AS)
api = tweepy.API(auth, wait_on_rate_limit=True)

cols = ['id']
# フォロワー格納用DataFrame
followers_ids = pd.DataFrame([], columns=cols)
# フォローしている人格納用DataFrame
followings_ids = pd.DataFrame([], columns=cols)

# フォロワー取得
itr = tweepy.Cursor(api.followers_ids, id='****', cursor=-1).items()   
for follower_id in itr:
    record = pd.Series([follower_id], index=followers_ids.columns)
    followers_ids = followers_ids.append(record, ignore_index=True)

# フォローしている人取得
itr = tweepy.Cursor(api.friends_ids, id='****', cursor=-1).items()   
for following_id in itr:
    record = pd.Series([following_id], index=followings_ids.columns)
    followings_ids = followings_ids.append(record, ignore_index=True)

# CSV出力
followers_ids.to_csv('../csv/followers.csv')
followings_ids.to_csv('../csv/followings.csv')

これを実行するとフォロワーのIDをリスト化したCSVファイルとフォローしている人のIDをリスト化したCSVが作成されます。

フォロワーのリスト
f:id:talosta:20210114163212p:plain

フォローしている人のリスト
f:id:talosta:20210114163358p:plain

なおここでいうIDはアカウント名とは異なります。

現時点ではIDが誰のものかわかりませんが、後ほどわかるのでここでは置いておきます。


スポンサーリンク



一方的にフォローしている人をリストアップする

こちらもフォルダを三か所で指定しているので、ご自身の環境に合わせて修正してください。

import tweepy
import pandas as pd

CK = '****'    # API KEY
CS = '****'    # API secret key
AT = '****'    # Access token
AS = '****'    # Access token secret

auth = tweepy.OAuthHandler(CK, CS)
auth.set_access_token(AT, AS)
api = tweepy.API(auth, wait_on_rate_limit=True)

# フォロワーのリストを読み込む
followers_ids = pd.read_csv('../csv/followers.csv', index_col=0)
# フォローしている人のリストを読み込む
followings_ids = pd.read_csv('../csv/followings.csv', index_col=0)

# 一方的にフォローしている人格納用DataFrame
oneside_follow = pd.DataFrame([], columns=['id', 'name', 'screen_name', 'description'])

# フォローしている人リストに存在していてフォロワーリストに存在していないユーザーを抽出
for following_id in followings_ids['id']:
    if following_id not in followers_ids['id'].values:
        # ユーザーの情報を取得
        user = api.get_user(following_id)
        record = pd.Series([user.id, user.name, user.screen_name, user.description], index=oneside_follow.columns)
        oneside_follow = oneside_follow.append(record, ignore_index=True)

# CSV出力
oneside_follow.to_csv('../csv/oneside_follow.csv')

これを実行すると一方的にフォローしている人の情報をリスト化したCSVが作成されます。

f:id:talosta:20210114164911p:plain

IDだけでなくユーザー名やアカウント名、自己紹介文も出力するようにしたので、確認してフォロー解除したくない人は消しておきましょう。

一方的にフォローしている人をフォロー解除する

こちらもフォルダを二か所で指定しているので、ご自身の環境に合わせて修正してください。

import tweepy
import pandas as pd

CK = '****'    # API KEY
CS = '****'    # API secret key
AT = '****'    # Access token
AS = '****'    # Access token secret

auth = tweepy.OAuthHandler(CK, CS)
auth.set_access_token(AT, AS)
api = tweepy.API(auth, wait_on_rate_limit=True)

# 一方的にフォローしている人リストを読み込む
oneside_follow = pd.read_csv('../csv/oneside_follow.csv', index_col=0)

# フォロー解除済みユーザー格納用DataFrame
removed_users = pd.DataFrame([], columns=['id', 'name', 'screen_name', 'description'])

# フォロー解除
for remove_id in oneside_follow['id']:
    user = api.destroy_friendship(remove_id)
    record = pd.Series([user.id, user.name, user.screen_name, user.description], index=oneside_follow.columns)
    removed_users = removed_users.append(record, ignore_index=True)

# CSV出力
removed_users.to_csv('../csv/removed_users_'+ datetime.datetime.now().strftime('%y%m%d%H%M%S') + '.csv')

これを実行するとフォロー解除し、フォロー解除したユーザーのリストを作成します。

f:id:talosta:20210114171129p:plain

実際にフォローが外れているか確認してみてください。

おわりに

Twitter APIの申請は大変ですがぜひ試してみてください。