デブのDEV日記

港区で働くデブによるDEV(DEVELOPE)やデブ飯の記録。そんな美味いならデブになっても構わない。

Pythonで文字列を指定文字数で分割したListを生成する

Pythonで与えられた文字数で分割する関数を検証したのでメモ。
意外にも標準で用意されていない??自分の調査不足か。。
なので自ら実装してみた。

◆仕様

  • 引数1:分割したい文字列
  • 引数2:分割したい文字数
  • 戻り値:指定文字列を分割したList。最後の文字列が引数2の文字長に満たない場合は、最後の文字列

◆例

  • 引数1:abcdef
  • 引数2:2
  • 戻り値:['ab', 'cd', 'ef']
  • 引数1:abcdef
  • 引数2:4
  • 戻り値:['abcd', 'ef']

ソースコード

# strの文字列をlenの桁数毎に分割して
# Listで返却する。
def splitByLen(str, length) :
    result = []
    i = 0
    while True :
        parts = str[i*length : (i+1)*length]

        if parts != '' : 
            result.append(parts)

        if len(parts) < length :
            break
        i = i + 1

    return result

あまり「While True」のループは基本的に好きではないけど一旦これで。
引数のstrのレングスと、lengthを使えば回避出来そうだけどこだわり特にないのでこのままでいいかと。

機械学習入門①〜機械学習って何?〜

空前のAIブームですが人工知能、機会学習、AIなど色んな用語が乱立し
混乱してよく分からないと言う人が多いのではないかと思います。

最近会社で勉強会を開催していますので、自分なりの理解を簡単にまとめていきたいと思います。

人工知能=AI

Wikipediaによると

コンピュータを使って、学習・推論・判断など人間の知能のはたらきを人工的に実現したもの。

と言う事。
正確な定義はなく、人間の知能のような働きをするソフトウェアと言う事です。
例えば、ソフトバンクのPepper君にも人工知能が内蔵されていますし、自動車の自動運転技術、ルンバなどのお掃除ロボットなどもそうです。
人間がやっているようなことをコンピューターが代替してくれる場合に大きい意味で人工知能と言うワードを使います。

では、一方で機会学習って言うのは何でしょう。

機械学習

ではどうやって人工知能を実現するのか?
と言う場合に登場するのが機械学習です。
つまり機会学習は、人工知能を実現するための方法の事です。
大量のデータからパターンを学ばせる方法が機械学習です。

よく例に使われるのが、迷惑メールの分類です。
大量のメールデータから、どれが迷惑メールかそうでないかを学習します。
分類には数式が使われます。
もっとも簡単な例だと「Y=AX+B」のようなグラフです。
このグラフを境界値として、直線の上部が通常のメール or 下部が迷惑メールのような分類をします。(超簡略化していますが実際の数式はもう少し複雑かと思われます。)
このデータの境界を決める数式を求めるのが機械学習です。

中学くらいの数学では、Y=2X+1のグラフにおいて、X=1の時のYを求めなさい、と言う問題が多いですよね。
そうではなく機械学習は全く逆です
大量のYとXを学習データとして渡すと言う事です。

そして大量の学習データ(XとY)からY=AX+B のAとBを求めて、分類グラフを求めるのが機械学習です。

そして、この分類の方法がアルゴリズムと言うものです。
分類の方法が沢山あるのは、データの境界線が単純に直線で分類出来るもの、曲線になるもの、2次元空間で分類できないので
3次元などのより高次元になるもの、線では分類できず、いくつかの島にグループ化されているものなどデータの分布形態が様々となってくるためです。

簡単ですが、自分の理解を纏めてみました。

次回は、いくつかの機械学習の種類について纏めたいと思います。

Selenium WebDriverでInternetExploreDriverが遅い場合の対処法

テスト自動化ツールのSeleniumを現在のプロジェクトで導入しようと奮闘中。
Selenium自体は、以前使っていた事もあったのでハードルは高くないかなと思っていましたが
いくつかハマりポイントが。。
バージョンが変わったせいかな・・・

Selenium自動テストの流れは大体↓のような流れかと。

  1. FirefoxのアドオンSeleniumIDEをインストールする。
  2. SeleniumIDEを起動し記録を開始する。
  3. 画面操作を行い操作を記録する。(右上の赤いボタン)こうする事で操作内容がHTMLとして吐き出される。
  4. 画面操作が終わったら記録をストップする。
  5. このHTMLを流すだけで自動テストは可能。ただし、JenkinsなどのCIツールと連携して定期的な自動テストをしようとするとこの状態では不可能。
  6. Selenium公式サイトからDriverServerと各クライアントの言語のライブラリ群をダウンロードする。
  7. 自動テストする場合は、JAVAならJUnit等で実施する必要があります。なんとjUnitのコードはSelenium IDEから吐き出せます!
  8. 吐き出したコードは、メンテを考えてスパゲッティーコードにならないよう共通処理を基底クラスに移すなど整形した方が良いです。

細かい説明を大分端折りましたが、↑の流れで自動化可能です。

いざ、実食!!

自動テスト始まりましたがタイピングスピードおそ!!!
お前のパンチ、止まって見えるぜ!

ググってみたものの日本語のサイトに有用な情報なく、いつもの通りJonとかBobとかがいるサイトに行きつく。

Hi、Jack!

みたいなアメリカンな文章を眺めつつ、読んでいくと
Selenium Serverが64bitで俺も同じ問題にぶち当たったが、32bitにしたら解決したぜ!!ハハ!!」
みたいな文章が見つかる。
Thank you! Tom
おお、解消してるのか。
早速、64bitバージョンのSeleniumServerを32bit版に差し替え実行したところ無事解決。

とう訳で今日も美味いビールが飲もう。



SimpleDateFormatのYYYYとyyyyの違いに要注意

クライアントより連絡があり、本番環境である年月日表示が合わないと調査依頼があった。
画面とCSVで確かに生年月日が合っていない。。
CSV側がの西暦がずれている。
「2015/12/28」と表記されなければいけないものが「2016/12/28」と表示されている。
1年余分に年が進んでしまっているではないか!

調べてみたところ実装で怪しい箇所を見つけた。
SimpleDateFormatに指定している年のフォーマットが大文字の「YYYY」となっているではないか。

SimpleDateFormatのJavaDocを見ると

Y 暦週の基準年

と記載があった。この記載は、Java7以降からかな。
暦週の基準年ってなんやねん、っていう事でググってみました。

暦週の基準年は、WEEK_OF_YEAR のサイクルと同期がとられます。最初の週と最後の週の間にあるすべての週 (両端の週を含む) の暦週の基準年は、同じ値になります。したがって、暦週の基準年が同じでも、最初の日と最後の日では暦年の値が異なる場合があります。

たとえば、1998 年 1 月 1 日は木曜日です。getFirstDayOfWeek() が MONDAY で getMinimalDaysInFirstWeek() が 4 (ISO 8601 規格に準拠した設定) の場合、1998 年の第 1 週は 1997 年 12 月 29 日に始まり 1998 年 1 月 4 日で終わります。 暦年が 1997 年の最後の 3 日については、暦週の基準年が 1998 になります。 ただし、getFirstDayOfWeek() が SUNDAY の場合、1998 年の第 1 週は 1998 年 1 月 4 日に始まり 1998 年 1 月 10 日に終わります。1998 年の最初の 3 日間は 1997 年の第 53 週に入り、それらの日の暦週の基準年は 1997 です。

f:id:devdebu:20170516142501p:plain

↓ここ注目です。
暦年が 1997 年の最後の 3 日については、暦週の基準年が 1998 になります。

1997年でも年末の最後の3日は「YYYY」を指定すると1998年が返却される模様です。

以下のコードで検証してみました。

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

public class DateFormatterTest {

	public static void main(String[] args) throws ParseException {

		System.out.println("===== 2015年 ====================================");

		SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
		Date date = sdf.parse("2015/12/26");
		SimpleDateFormat sdf1 = new SimpleDateFormat("YYYY/MM/dd");
		String date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy/MM/dd");
		String date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/27");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/28");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/29");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/30");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2015/12/31");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		System.out.println("===== 2012年 ====================================");
		date = sdf.parse("2012/12/29");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);

		date = sdf.parse("2012/12/30");
		date1 = sdf1.format(date);
		System.out.println("YYYY→" + date1);

		date2 = sdf2.format(date);
		System.out.println("yyyy→" + date2);
		System.out.println("=================================================");

	}
}

実行結果

===== 2015年 ====================================
YYYY→2015/12/26
yyyy→2015/12/26
YYYY→2016/12/27
yyyy→2015/12/27
YYYY→2016/12/28
yyyy→2015/12/28
YYYY→2016/12/29
yyyy→2015/12/29
YYYY→2016/12/30
yyyy→2015/12/30
YYYY→2016/12/31
yyyy→2015/12/31
===== 2012年 ====================================
YYYY→2012/12/29
yyyy→2012/12/29
YYYY→2013/12/30
yyyy→2012/12/30
=================================================

2015年は、12/26まで正しく表示されていますが、12/27になると急に年がずれはじめてます。
2012年は、12/29まで正しく表示されていますが、12/30になると急に年がずれはじめてます。

YYYYを指定すると1月1日が所属する週にいる12月の日付に関して、+1年で年を返すようです。

年末年始返上になりかねない嫌がらせのような仕様です・・・

HTMLのサニタイズ処理(Spring)

サニタイズ処理をしてくれるメソッドがないかとApache Commonsを調べていたら
StringEscapeUtilsにそれらしきメソッドescapeHtmlを発見したやん!!

使ってみたところ、日本語もサニタイズされてしまうやん。。
サニタイズしたいのは<>&とかのこういうタグとかだけなので使えなかった。。。
commons langのバージョン3以降で escapeHtml4とかいうのがあったけど、これだけの為にcommonsのバージョン勝手に
あげられないのでSpringで探してみた。

お、あったやん!!
HtmlUtilsにhtmlEscapeなるメソッドを発見!!

HtmlUtils.htmlEscape(message.getMessage());

これで期待通りの動きをしてくれた。
めでたしめでたし。

【JAVA】固定長での文字列分割(split)

Stringクラスのsplitメソッドは、文字列分割で比較的よく使う。


  • 第2引数指定無しで分割する。

これはよく使う使い方ですね。

String str = "a,b,c,d,e";
String[] splits = str.split(",");
-- 実行結果:[a][b][c][d][e]
  • 第2引数を指定して分割する。

第2引数を指定すると分割の最大長を指定出来ます。
この例だと引数指定無しだと5分割されますが、第2引数に3を指定すると3分割されます。

String str = "a,b,c,d,e";
String[] splits = str.split(",", 3);
-- 実行結果:[a][b][c, d, e]

先頭から分割して第2引数の最大分割に達すると残りは全て最後の配列に格納されますね。

【JAVA】全角スペースでtrimする

Stringクラスのtrimメソッドは、半角スペースにしか対応していないので全角スペースもtrimしてくれるメソッドないかな、と探していました。
JAVA標準のAPIではまず無いだろうな、と思っていたので
とりあえずcommonsのStringUtilsを探してみました。
stripメソッド!発見しました。まさしく探し求めてたメソッド

第1引数:処理対象文字列
第2引数:trimする文字列

□実験コード

	String str = StringUtils.strip(" abc ", "  ");
	System.out.println("文字列 :" + str + "*+*+*");

	str = StringUtils.strip(" abc  ", "  ");
	System.out.println("文字列 :" + str + "*+*+*");

	str = StringUtils.strip(" a b c  ", "  ");
	System.out.println("文字列 :" + str + "*+*+*");

	str = StringUtils.strip("a     b c", "  ");
	System.out.println("文字列 :" + str + "*+*+*");

	str = StringUtils.strip("  a     b c ", "  ");
	System.out.println("文字列 :" + str + "*+*+*");

□実行結果

文字列 :abc*+*+*
文字列 :abc*+*+*
文字列 :a b c*+*+*
文字列 :a     b c*+*+*
文字列 :a     b c*+*+*

やるやん!!!