TalendでtJavaを使用してJavaでマスキング処理。

talend

はじめに

Talendには様々なコンポーネントが用意されていますが、細かいところはコードを書いて対応したいという事があります。その時には「tJava」を使用すれば解決します。
TalendはEclipseを元に開発されている様なので、プログラミング画面はまんまEclipseです。

環境

Mac OSX 10.10.5 Yosemite
Talend Open Studio for Data Integration 5.6.2
JDK 1.7.0_79

プログラムの目的

TSVファイルの項目別の対応でマスキングし、新規TSVファイルを出力する。

TSV

id  name  post  address tel
12345678901 madonna louise ciccone  010-1101  神奈川県横浜市XX区YY町111  045-011-0011
23456789012 prince rogers nelson  011-0010  埼玉県三郷市YY町010  070-1100-1100
34567890123 michael joseph jackson  110-1001  千葉県船橋市YY町101  090-1001-0110
45678901234 中田 ヤステル 222-2222  東京都中野区XX222 080-2222-2222
56789012345 小室 哲夫 333-3333  東京都府中市YY町333  080-3333-3333

左から、登録番号、登録者名、郵便番号、住所、電話番号。

Talend:コンポーネント配置

blog_talend_masking_job

Talend:コンポーネント設定

tFileInputDelimited

TSVを読み込むコンポーネント。

基本設定

blog_talend_masking_tFileInputDelimited_1
ファイル名/ストリーム: 読み込むファイルパス
行区切り記号: 改行コード
フィールド区切り記号: 今回はTSVなので「\t」
ヘッダー: TSVのヘッダーは不要なので「1」
スキーマの編集: 押下してTSVとレイアウトを合わせたものを作成。「➕」でカラム追加できます。

blog_talend_masking_tFileInputDelimited_3

Advanced setting

blog_talend_masking_tFileInputDelimited_2
エンコード: 任意の文字コード(今回はUTF-8)

tJava

Javaコードを書いてメソッドを定義できます。
使用するメソッドは下のの様に書いておきます。コード内容は後述。
スクリーンショット 2016-10-14 11.59.06

tMap

tMapアイコンをダブルクリックするか、「コンポーネント」>「基本設定」>「マップエディタ」を押下してウィンドウを開き、下の画像の様に設定します。
blog_talend_masking_tmap
カラム名をドラッグして真ん中のウィンドウ(「Var」と表示されている箇所)に持っていくとtMapに登録されます。
項目「式」にtJavaで設定したメソッドを記述できます。
設定したら、「tFileOutputDelimited」の対応するカラム名にドラッグして接続して完成。
最初はカラム名が表示されていないと思うので、先に「tFileOutputDelimited」で「カラム名の同期」を実行しておくと楽です。

各カラムのtMapの設定は下記の様にします。

SampleClass.maskingId(row2.id)
SampleClass.toHeadUpper(SampleClass.maskingName(row2.name))
SampleClass.maskingPostal(row2.postal) 
SampleClass.maskingAddress(row2.address)
SampleClass.maskingNumber(row2.tel) 

tFileOutputDelimited

TSVを出力します。

基本設定

blog_talend_masking_tFileOutputDelimited_1
ファイル名: TSVファイルの出力先パス
行区切り記号: 改行コード
フィールド区切り記号: TSVなので「\t」
ヘッダーを含むにチェックを入れれば、Talendで設定したカラム名がヘッダーになります。

Advanced settings

blog_talend_masking_tFileOutputDelimited_2
ここはお好みで。

tJavaでコード作成

準備

1、画面左のリポリジトリの「コード」を右クリックして「フォルダを作成」。
blog_talend_routine_1
2、作成したフォルダを右クリックして「コードを作成」。
3、Eclipseライクなプログラミング画面が表示されます。

必要なメソッド

今回はカラムごとにマスキング方法が違うので、下記を用意します。

カラム名 対応するメソッド 内容
id maskingId 登録ID。3〜7桁目を「*」に置換。
name maskingName 登録者名。最初と最後の文字を「*」に置換。
name toHeadUpper 登録者名の頭文字が半角英字の場合に頭文字を大文字にする。
postal maskingPostal 郵便番号。「-」を削除し、下4桁を「*」に置換。
address maskingAddress 住所。2〜4、6〜8、10〜14文字目を「*」に置換。
tel maskingNumber 電話番号。「-」以外を全て「*」に置換。

マスキング処理のコード作成

package routines;

public class SampleClass {

	/**
	 * {talendTypes} String
	 * {Category} User Defined
	 * 3〜7文字を「*」にする。
	 * @return
	 */
	public static String maskingId(String input) {
		StringBuilder sb = new StringBuilder();
		
		if (input != null) {
			// 引数の文字列を分割
			for (int i = 0; i < input.length(); i++) {
				// 分割した文字の3〜7文字目()を「*」に置換。
				if (i>=2 && i<=6) {
					sb.append("*");
				}
				// それ以外はそのまま。
				else {
					sb.append(input.substring(i, i+1));
				}
			}
			
			return sb.toString();
		}
		
		return input;
	}

	
	/**
	 * {talendTypes} String
	 * {Category} User Defined
	 * 引数の文字列の最初と最後を「*」に置換する。
	 * @param input
	 * @return
	 */
	public static String maskingName(String input) {
		// 前後の空白を削除
		input = input.trim();
		// 名前の文字数を取得
		int count = input.length();

		// 引数の文字数で分岐
		if (count > 1) {
			// 準備
			String asterisk = "*";
			String output = "";
			StringBuilder sb = new StringBuilder();
			
			for (int i = 0; i < count; i++) {
				// 最初と最後は「*」
				if (i==0 || i==count-1) {
					sb.append(asterisk);
				} else {
					sb.append((input.substring(i, i+1)));					
				}
			}

			output = sb.toString();

			return output;

		} else if (count == 1) {
			// 1文字なので「*」を返す。
			return "*";
		} else {
			// 0文字なので入力された引数をそのまま返す。
			return input;
		}
	}	
	
		
	/**
	 * {talendTypes} String
	 * {Category} User Defined
	 * 引数の文字列をfirst,middle,lastに分け、それぞれの頭文字を大文字にする。
	 * @param input
	 * @return
	 */
	public static String toHeadUpper(String input) {
		// 準備
		StringBuilder sb = new StringBuilder();
		String output = "";

		// 1.文字列を空白で分割
		String[] strSplit = input.split(" ");
		int splitLength = strSplit.length;

		// 2.分割した要素分(苗字と名前など)、頭文字を大文字にする			
		for (int j = 0; j < splitLength; j++) {
			sb.setLength(0); // ループ毎に初期化

			// 3.頭文字を抽出
			String top = strSplit[j].substring(0,1);
			
			// 4.頭文字が正規表現で先頭が[a-z]の場合
			// 正規化の準備(英字小文字の場合は頭文字を大文字にする)
			Pattern p = Pattern.compile("^[a-z]"); // 文字列の先頭が[a-z]
			if (p.matcher(top).find()) {
				
				// 4-1.文字目以降の文字列を取得
				String end = strSplit[j].substring(1, strSplit[j].length());
				
				// 4-2.先頭を大文字に置換
				sb.append(top.toUpperCase());
				sb.append(end);
				
			} else {
				// 4.先頭が英字小文字でない場合はそのままSBに結合
				sb.append(strSplit[j]);
			}

			// 5.名前同士の間に半角空白入れる(最後じゃない場合)
			if (j != splitLength-1) {
				sb.append(" ");
			}

			output += sb.toString(); 
		}

		// 最初と最後を「*」に置き換えた文字列を返す。
		return output;
	}


	/**
	 * {talendTypes} String
	 * {Category} User Defined
	 * "-"を削除。郵便番号で使用。
	 * @return
	 */
	public static String maskingPostal(String input) {
		// 「-」を削除して、右から4桁を「*」に置換
		return input.substring(0, 3) + "****";
	}


	/**
	 * {talendTypes} String
	 * {Category} User Defined
	 * 住所の2〜4、6〜8、10〜14文字目を*に置換する。
	 * @return
	 */
	public static String maskingAddress(String input) {
		if (input != null) {
			StringBuilder sb = new StringBuilder();

			// 引数の文字列を分割
			for (int i = 0; i < input.length(); i++) {
				// 分割した文字の2〜4、6〜8、10〜14文字目を「*」に置換。
				if ((i>=1 && i<=3) || (i>=5 && i<=9) || (i>=11 && i<=15)) {
					sb.append("*");
				}
				// それ以外はそのまま。
				else {
					sb.append(input.substring(i, i+1));
				}
			}
			
			// 文字列を返す。
			return sb.toString();
		}
		
		return input;
	}

	
	/**
	 * {talendTypes} String
	 * {Category} User Defined
	 * 電話番号が"-"以外を「*」に変換して返す。
	 * @param number 電話番号。
	 * @return String
	 */
	public static String maskingNumber(String number) {
		char[] c = number.toCharArray();
		StringBuilder sb = new StringBuilder();

		/*
		 * Stringをchar配列に分割し、一文字ずつ条件に当てはめて行く。
		 * '-'ではなく、15文字より手前なら、「*」をStringに入れる。
		 * それ以外は、charをそのままStringに入れる。
		*/
		for (int i = 0; i < c.length; i++) {
			if (c[i]!='-') {
				sb.append("*");
			}
			else {
				sb.append(Character.toString(c[i]));
			}
		}

		// 文字列を返す
		return sb.toString();
	}	
}

注意点:メソッドは「public static」とします。
「private」にするとtMapで利用できなかったと思います。

また、メソッドのドキュメンテーションコメントに下記を書いておくとtMapの式ビルダのカテゴリ「ユーザー定義」に表示されます。

/**
 * {talendTypes} String
 * {Category} User Defined
 */

実行結果

id	name	postal	address	tel
12*****8901	*adonna Louise Ciccon*	010****	神***横*****Y*****	***-***-****
23*****9012	*rince Rogers Nelso*	011****	埼***郷*****1*	***-****-****
34*****0123	*ichael Joseph Jackso*	110****	千***橋*****0*	***-****-****
45*****1234	*田 ヤステ*	222****	東***野*****2	***-****-****
56*****2345	*室 哲*	333****	東***中*****3*	***-****-****

さいごに

今回は文字をマスキングする方法を書きましたが、カラム自体を隠してしまう方法もあります。