Java

みんなのFX 取引報告書(日次)から取引履歴を抜き出す

始めに

みんなのFX 取引報告書(日次)から取引履歴を抜き出すプログラムを書いてみました

PDFからテキストファイルを取り出すプログラム

BiFunctionを使っているのは単純にテキストファイルを取り出す処理とPDFをイメージ化してテキスト化する処理を簡単に切り替えられるようにするため

TextFileMaker.java
package pdf;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiConsumer;

import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.rendering.ImageType;
import org.apache.pdfbox.rendering.PDFRenderer;
import org.apache.pdfbox.tools.ExtractText;


public class TextFileMaker {
    private static String[] FILES={
            "cdtr_514537361342119445.pdf",
            "cdtr_514818836318830101.pdf",
            "cdtr_515100311295540757.pdf",
            "cdtr_515944736225672725.pdf",
            "cdtr_516226211202383381.pdf",
            "cdtr_516507686179094037.pdf",
            "cdtr_516789161155804693.pdf",
            "cdtr_517070636132515349.pdf"           
    };
    public static void main(String args[]) {
        Arrays.stream(FILES)
        .forEach(s->exectute(TextFileMaker::createTextFile,
                String.format(args[0], s),
                String.format(args[1], s)));
    }

    public static void exectute(BiConsumer<String, String>bi,String from,String to){
        bi.accept(from, to);
    }
    public static void createTextFile(String from ,String to){
        try{
            ExtractText.main(new String[]{from, to});
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static void writeToTextFile(String from,String to){
        try{
            Files.write(Paths.get(to), 
                    createTextFromImage(PDDocument.load(new File(from))), 
                    Charset.forName("MS932"),
                    StandardOpenOption.CREATE);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static List<String> createTextFromImage(PDDocument doc) throws Exception{
        List<String>list = new ArrayList<String>();
        PDFRenderer pdfRenderer = new PDFRenderer(doc);
        for(int i=0;i<doc.getPages().getCount();i++){
            list.add(PDFtoImg.extractFromPDF( 
                    pdfRenderer.renderImageWithDPI(i, 300, ImageType.RGB)));
        }
        return list;
    }
}

テキストファイルから取引履歴を取り出すプログラム

PDFから取得できるテキストファイルのフォーマットがコンピュータ解析に全く向いていないので、強引にフォーマットを合わせていたりします

ProfitAnalyser.java
package fx;

import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ProfitAnalyser {
    private static String[] FILES={
            "cdtr_514537361342119445.pdf",
            "cdtr_514818836318830101.pdf",
            "cdtr_515100311295540757.pdf",
            "cdtr_515944736225672725.pdf",
            "cdtr_516226211202383381.pdf",
            "cdtr_516507686179094037.pdf",
            "cdtr_516789161155804693.pdf",
            "cdtr_517070636132515349.pdf"           
    };

    public static void main(String args[]){
        Arrays.stream(FILES)
        .map(s->String.format("work/fx/%s.txt", s))
        .forEach(ProfitAnalyser::printstr);
    }

    public static void printstr(String str){
        try{
            Files.lines(Paths.get(str), Charset.forName("UTF-8"))
            .reduce(new DataStruct(),ProfitAnalyser::execute,(s,v)->v)
            .dealInfo
            .forEach(System.out::println);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static DataStruct execute(DataStruct struct ,String str){
        return analyser(makeData(struct,str),str);
    }

    public static DataStruct analyser(DataStruct struct ,String str){
        if(str.matches("^\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}.*")){
            String[] strs= str.replaceAll("\\d{4}\\-\\d{2}\\-\\d{2}", " ").split(" ");
            if(strs.length<3){
                return struct;
            }

            String result = struct
            .currentPair
            .stream()
            .filter(s->s.indexOf(strs[0] + " " + strs[1])>=0)
            .findFirst()
            .get();
            String currentPair = result.split(",")[1];
            struct.dealInfo.add(strs[0]+" "+strs[1] +","+strs[2].replaceAll(",", "")+","+currentPair);
            return struct;
        }else{
            return struct;
        }
    }

    public static DataStruct makeData(DataStruct struct,String str){
        struct.currentPair.add(parseCurrentPair(str));
        return struct;
    }

    public static String parseCurrentPair(String str){
        return getFirstMatched(".*(\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}).*決済(\\w{6}).*",str);
    }
    public static String getFirstMatched(String reg,String str){
        Pattern p = Pattern.compile(reg);
        Matcher m = p.matcher(str);
        if(m.find()){
            return m.group(1)+","+m.group(2);
        }
        return "";
    }
    private static class DataStruct{
        public List<String> currentPair = new ArrayList<String>();
        public List<String> dealInfo = new ArrayList<String>();
    }
}

実行結果

こんな感じの結果が得られます

2018/01/03 11:24:13,-1580,EURJPY
2018/01/03 11:24:17,1050,AUDJPY
2018/01/03 16:07:00,670,AUDJPY
2018/01/03 19:32:56,1475,EURJPY
2018/01/03 19:54:43,1765,EURJPY