技術とか戦略とか

証券レガシーシステムを8年いじってから転職した普通の文系SEによるブログ。技術のみではなく趣味の戦略考察についても。

viで開けない巨大なファイルを開く(改行コード無し固定長レコード編)

4GB以上の改行コード無し固定長レコードのファイルの中から一部のレコードを抜き出す機会があったのですが、viエディタでも開けず、Windowsに落としてもエディタ(サクラエディタ、Stirling)でも開けなかったので、以下のように対処しました。
cutコマンドを使用して開ける大きさのファイルにするのがミソです。
 
1.下記コマンドで必要なレコードの存在位置にあたりをつける
例えば、レコード長100バイトで100レコード(合計10000バイト)のファイルについて、キー項目「1234567890」の存在位置を調べたい時、以下のコマンドによりあたりをつけることができます。
 
cut -b 1-5000 hoge | grep "1234567890" | wc -l
 
まず、cutコマンドにより、1バイト目から5000バイト目を抜き出しています。
次に、抜きだした結果に対して「1234567890」という文字が存在するかどうか調べています。
存在していれば、その次のwcコマンドに標準出力が渡り、1が出力されます。
存在しなければ、標準出力は渡らないので、0が出力されます。
 
2.徐々に範囲を狭める
例えば、1-5000バイトにキー項目が存在していた場合は、次に以下のようにコマンドを発行します。
 
cut -b 1-2500 hoge | grep "1234567890" | wc -l
 
これでもし1-2500バイトの範囲にキー項目が存在しなければ、次は以下のようにコマンドを発行し、2501-5000バイト目のどこかにキー項目が存在することを確認します。
 
cut -b 2501-5000 hoge | grep "1234567890" | wc -l
 
このように、二分検索の要領で、徐々に範囲を狭めていくのがポイントです。
 
3.レコードを抜きだす
開ける大きさにまで範囲が狭まったら、「| grep "1234567890" | wc -l」の箇所をリダイレクトに変更してファイルを出力します。
 
cut -b 2501-5000 hoge > hoge_tmp
 
なお、抜きだしたファイルには改行コードが付与されているので、下記コマンドで改行コードを削除します。
 
tr -d '\n' < hoge_tmp > hoge_tmp2
 
これで、目的のレコードを含む開ける大きさのファイルを抽出できます。
 
--------------------
 
なお、今回私はやっていませんが、一部レコードを編集した後、最終的に全レコードを結合し直す必要がある場合は、以下の手順でできるはずです。
①cutコマンドで「編集対象のレコードの前のファイル」「編集対象のレコードを含むファイル」「編集対象のレコードの後ろのファイル」の3つに分割する。
②「編集対象のレコードを含むファイル」を編集する。
③「編集対象のレコードの前のファイル」「編集対象のレコードを含むファイル」「編集対象のレコードの後ろのファイル」をcatコマンドで結合する(trコマンドで改行コード削除も行う)。

データが多すぎてExcelが重い→ファイル分割、Excel以外のプログラムを使って対応

数十万件のデータをExcelで扱っていて、どうにも重い、メモリ不足で落ちる、といったことがありました。
ググると「データを減らす」という対応法が載っていますが、数十万件のデータを一度に扱わないといけなかったのでこれは困難でした。
また、データをただ入力して関数で計算していただけなので、条件付き書式等の高尚なことはしていませんし、見えない所にゴミデータがあるということもありませんでした。
 
色々試行錯誤した結果、以下の2つが効果があったので紹介します。
 
1.シートを分ける代わりにファイルを分ける
何段階かに分けて処理を行っていたため段階毎にシートを分けていたのですが、シートで分ける代わりにファイル(ブック)で分けたら一気に処理が軽くなりました。
理由は後で調べたのですが、どうやらExcelはファイル毎にプロセスが分かれているみたいです。ファイルで分けることでマルチプロセス状態となったために、処理速度が向上しました。
なお、Excel2010までは意識せずともマルチプロセスになるのですが、Excel2013やExcel2016の場合は「ファイル名を指定して実行」で「excel /x」というコマンドを与えないとマルチプロセスにならないようなので注意が必要です。
 
2.Excel以外のプログラムを使って対応
これを言ってしまったら元も子もないのですが、現実問題としてExcel以外のプログラムで処理できることはExcel以外のプログラムで処理した方が良いです。
例えば、ソート・マージ、区切り文字や改行コードを使った簡単なフォーマットの変更、といった作業なら、サクラエディタを使った方が断然早いです。
IT業界に勤めているのであれば、場合によってはjava等で簡単なプログラムを作っても良いかもしれません。
なお、vlookup関数の高速化でググったら、「オープンソースRDBMSを使えば高速で処理できるようになるよ!」という(ちょっとワイルドな)例もありました。

ヘッダレコード・データレコード・トレーラレコードとは

企業間でやりとりするファイルでよく見かけるフォーマットとして、レコードが「ヘッダレコード」「データレコード」「トレーラレコード」に分かれているフォーマットがあります。
このフォーマットについて覚えておくと、出向先のソースコードを読むのが楽になったり他社とのやりとりが楽になります。
以下で、「ヘッダレコード」「データレコード」「トレーラレコード」の簡単な説明と使用例を記載します。
 
【それぞれのレコードの説明】
・ヘッダレコード
 ファイルの1レコード目のレコード。
 一般的には、そのファイルが何日のデータなのかが記載される。
 
・データレコード
 ファイルの中間レコード。
 実際にやりとりするデータの中身が記載される。
 
・トレーラレコード
 ファイルの最終レコード。
 一般的には、そのファイルのデータレコード件数が記載される。
 
【フォーマット例】
取扱商品ファイルを想定した例を記載します。
可変長のCSVファイルを想定します。
(固定長の場合も多いです。固定長の場合は、カンマ区切りではなくバイト数で区切ることになります。)
 
・ヘッダレコード
 1項目目:ファイル区分(1がセットされる)
 2項目目:ファイル作成日付(YYYYMMDD)
 
・データレコード
 1項目目:ファイル区分(2がセットされる)
  ※5がセットされることも多いです
 2項目目:商品コード(7桁の数値)
 3項目目:商品名(全角文字)
 4項目目:値段(数値)
 
・トレーラレコード
 1項目目:ファイル区分(9がセットされる)
 2項目目:データレコードの件数(数値)
 
【ファイルのレコード例】
1,20190602
2,0000001,ほげ商品,100
2,0000002,ふが商品,250
2,0000003,ぴよ商品,1000
9,3
 
【ファイル受信時の処理例】
ヘッダレコードやトレーラレコードは、受信側のチェック処理に利用できます。
以下、ファイルを1レコード目から順番に読む場合の処理例について記載します。
 
・ファイル区分が1の時
 1レコード目がファイル区分1でなければ異常終了。
 (送信側が中間ファイル等誤ったファイルを送信することを想定)
 バッチ日付と比較し一致しなければ異常終了。
 (送信側が誤って過去ファイルを送信することを想定)
 
・ファイル区分が2の時
 業務上必要な処理を実行。
 
・ファイル区分が9の時
 最終レコードのファイル区分が9でなければ異常終了。
 (ファイルを全件受信できていないことを想定)
 件数がファイル区分2のレコード数と一致しなければ異常終了。
 (ファイルを全件受信できていないことを想定)

アンカリングを用いた交渉術

アンカリングとは、先に与えられる情報をベースに意思決定してしまうことを指す心理面の現象のことです。
この現象は交渉の場面で役に立ちます。
先に厳しめの要求を出すことで、その要求をベースに交渉を進めることができます。
厳しめの要求を出した後、相手に合わせて譲歩するのですが、厳しめの要求をベースに譲歩をすることができるので、こちらが飲めるギリギリのラインよりもこちらにとって有利なラインで交渉を成立させることができます。
 
技術職としては、工数の見積もりの場面で応用できます。
できることがわかりきっている案件であれば正直に見積もりを出せば良いのですが、不確実性がある案件ではある程度のリスクバッファが必要になります。
「不確実性コーン」と呼ばれる有名な研究成果があるのですが、最も初期の段階では見積もり工数に±400%のブレが発生します(以下のページの図がわかりやすいです)。
 
西尾泰和のScrapbox

https://scrapbox.io/nishio/%E4%B8%8D%E7%A2%BA%E5%AE%9F%E6%80%A7%E3%82%B3%E3%83%BC%E3%83%B3

 
そこで、リスクバッファを確保するために、交渉で工数が削られることを見越して多めの工数で見積もるということをします。
発注側が想定している工数から10倍も離れている場合は流石に「いやいやそれは…」という話になりますが、その場合はこちら側が立派なものを作ろうとしすぎていることが多いので、お互いに想定している要件を話した上で要件のすり合わせをするということになります。
 
なお、多めの工数を出す理由としては「楽をしたい」とか「儲けたい」とかが理由ではなくあくまでもリスクバッファの確保なので、作業者が開発期間の長さに安心して平時からリスクバッファを食いつぶすようなことがあってはなりません。
そのため、開発者には逆に厳しめのスケジュールを伝えて、スケジュールの交渉(調整)をしたりします。そうすることで、スケジュール的な余裕が生まれ、不測の事態が起きた時に取れる選択肢が広がります。

ジョブ管理システムとは

「ジョブ管理システム」とは、処理の実行タイミングや実行順を管理するシステムのことです。
例えば、Windowsではbatファイルをダブルクリックすると処理が実行されますし、Linuxでは「sh hoge.sh」と入力すると処理が実行されます。実運用では、システムをまたいで決められたタイミングで決められた順番に処理を実行する必要する必要があるのですが、それを自動化してくれるものが「ジョブ管理システム」です。
 
具体的な製品名で言うと、「JP1」や「A-AUTO」といったものが挙げられます。
現場では製品名で会話されることが多いのですが、現場の先輩社員から「本番では運用担当がhogeを使って処理を実行するからうんぬんかんぬん」という会話を聞いたら、「ああ、きっとhogeはジョブ管理システムの製品名なんだろうな~」と思えば良いです。
 
なお、「JP1」や「A-AUTO」の製品内容は、以下に詳しく解説されています。
実際の運用画面も掲載されているので、イメージしやすいと思います。
 
JP1ジョブ管理 アシスト

https://www.ashisuto.co.jp/product/category/system-management/jp1/detail/solution/automatic-job-management.html

 
A-AUTO 50 - A-AUTO 50 とは

https://a-auto50.unirita.co.jp/about/

情報処理技術者試験対策「見積もり手法」

情報処理技術者試験で出題されることがある見積もり手法について、紹介していきます。
今回紹介するのは、主に大規模なウォーターフォールのプロジェクトで使われる見積もり手法です。
 
なお、実態としては、直感的に決めてしまうこともあります。
例えば、アジャイルではストーリーポイント(https://akira2kun.hatenablog.com/entry/2018/07/08/002646)のような直感的な手法が用いられますし、小規模な保守案件だと有識者が感覚で決めることも少なくありません。
しかし、見積もり手法を学ぶことでその直感の精度も上がるので、たとえ直接的に使わなかったとしても見積もり手法の発想を学んでおいて損はありません。
 
ボトムアップ見積もり】
WBSをもとに作業工数を積み上げて算出する見積もり手法です。
「標準見積もり」とも呼ばれます。
 
【類推見積もり】
類似する案件の工数を参考にして工数を見積もる手法です。
 
【三点見積もり】
楽観値、悲観値、最可能値(最も生起確率が高い値)の三点を算出する手法です。
 
パラメトリック見積もり】
過去の案件の分析結果を元に、パラメータ(係数)を用いて見積もりを行う手法です。
具体的には以下のようなものがあります。
 
・ハルステッドモデル
ネットで調べると「プログラムステップ数から規模を見積もる手法」と出てきますが、正確に言うとプログラムの中に出現する演算子と被演算子の数を数えることで規模を見積もる手法です。
(「演算子」(オペレータ)とは「+」とか「-」といったどのような計算を行うのかを示す要素であり、「被演算子」(オペランド)とはそれらの計算を行う対象となる数字や変数のことを指します)
 
ファンクションポイント法
機能ごとに複雑さに応じたポイントを与えることで、規模を見積もる手法です。
言葉で説明するよりも以下のページに掲載されている表を見て直感的に把握した方がわかりやすいと思います。

見積もり仮想体験の旅へ、いざ出発! (1-3) - ITmedia エンタープライズ

https://www.itmedia.co.jp/im/articles/0901/15/news130.html

 
・COCOMO
ベームという研究者が63件の案件を分析して作成した算術的な計算式により見積もりを行う手法です。
工程ごとに3つのモデルが存在し、プログラムステップ数をベースに、チームの規模や技術力や案件の制約に応じた補正係数をかけ合わせて規模を算出します。
詳しいことは以下のページに説明されています。

COCOMO (Constructive Cost Model)によるプロジェクト工数の見積もり

http://itref.fc2web.com/management/cocomo.html

 
・putnamモデル
開発工数に関するマンパワー曲線(どの工程でどの程度人数をかけるのかを示した曲線)の概念を適用した見積もり手法です。
運用・保守工程を含めたシステムライフサイクル全般をカバーできるのが特徴であり、総工数から各工程の工数を見積もることができます。
 
・Dotyモデル
比較的小規模な案件に適した分析手法です。
開発規模から開発工数を決定できる統計的な関係式を提供するベースライン型モデルと、開発環境の要員も考慮できる多変量型モデルが存在します。
 
【その他の見積もり手法】
以下のような手法があります。
 
Delphi
「多数の専門家に見積もり依頼を出す→見積もり結果の集約→再度見積もり依頼を出す…」を繰り返すことで、見積もり結果の収束を行うという手法です。
----------------------
目次

https://1drv.ms/b/s!AivF3bzWXOzuhG1Xk5hscKYqkLkM

 

java:インターフェースを用いることでクラス毎の重複した記述を無くす

javaにはインターフェースという機能があります。
インターフェースとは、メソッドの仕様(メソッド名、戻り値、引数)のみを定義したものです。
インターフェース単独では処理を実行できませんが、そのインターフェースを実装したクラスを定義することで処理を実行可能になります。
 
インターフェースを利用するメリットとしては、重複した記述を無くせることがあります。
オブジェクトを参照する際にインターフェース名を指定することで、そのインターフェースを実装している全てのクラスを指すことができます。一つの記述で複数のクラスに対応させることができるので、クラスごとに同じ記述を行う必要が無くなり、保守性が向上します。
デザインパターンでもインターフェースは良く使われます)
 
言葉だと通じにくいと思うので、サンプルコードを用意しました。
 
【サンプルコード】
・FinancialProducts.java
package lesson1;

/* 金融商品(インターフェース) */
public interface FinancialProducts {
    void passedAMonth(); // 1月過ぎた時の利息計算メソッド
}

・SingleInterestProducts.java
package lesson1;

/* 単利商品 */
public class SingleInterestProducts implements FinancialProducts {
    int principal = 100; // 元金
    int price = principal; // 商品価格
    @Override
    public void passedAMonth() {
        price = (int) (price + principal * 0.1);
        System.out.println("現在の商品価格:"+price);
    }
}

・ComplexInterestProducts.java
package lesson1;

/* 複利商品 */
public class ComplexInterestProducts implements FinancialProducts {
    int principal = 100; // 元金
    int price = principal; // 商品価格
    @Override
    public void passedAMonth() {
        price = (int) (price + price * 0.1);
        System.out.println("現在の商品価格:"+price);
    }
}

・Main.java
package lesson1;

public class Main {

    public static void main(String[] args) {

        /* オブジェクトの生成 */
        SingleInterestProducts singleInterestProducts
            = new SingleInterestProducts();
        ComplexInterestProducts complexInterestProducts
            = new ComplexInterestProducts();

        /* 共通処理の実行 */
        System.out.println
            ("SingleInterestProductsオブジェクト(単利商品)");
        passedTwoMonths(singleInterestProducts);
        System.out.println
            ("ComplexInterestProductsオブジェクト(複利商品)");
        passedTwoMonths(complexInterestProducts);
    }

    /* 2月過ぎた時の利息計算メソッド */
    public static void passedTwoMonths
        (FinancialProducts financialProducts) {
        financialProducts.passedAMonth();
        financialProducts.passedAMonth();
        /* インターフェースを使っていないと、
         * SingleInterestProducts用とComplexInterestProducts用で
         * 別々にメソッドを定義しなければならなかった */
    }
}

【実行結果】
SingleInterestProductsオブジェクト(単利商品)
現在の商品価格:110
現在の商品価格:120
ComplexInterestProductsオブジェクト(複利商品)
現在の商品価格:110
現在の商品価格:121