tag:qiita.com,2005:/sato_ken09/feed sato_ken09の記事 - Qiita Qiitaでユーザーsato_ken09による最近の記事 2024-11-27T18:51:22+09:00 https://qiita.com/sato_ken09 tag:qiita.com,2005:PublicArticle/1942009 2024-11-27T18:51:22+09:00 2024-11-28T12:30:59+09:00 https://qiita.com/sato_ken09/items/71cbde6fdba8a25b39e8 差分ツール import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Fl… sato_ken09 tag:qiita.com,2005:PublicArticle/1893505 2024-07-30T06:19:02+09:00 2024-07-30T22:42:28+09:00 https://qiita.com/sato_ken09/items/b61e0ac9c29a275d61cc 必要なJREをだけを抽出しEXE化するPOM 必要なJREをだけを抽出しEXE化するPOM JREを含む実行可能なJARファイルの作成 Windows用のEXEファイルの生成 カスタムJREの作成と配布用パッケージへの組み込み これらの目標を達… sato_ken09 tag:qiita.com,2005:PublicArticle/1884200 2024-07-09T14:40:59+09:00 2024-07-09T14:40:59+09:00 https://qiita.com/sato_ken09/items/d2ec018288842150e30d Java CSV 比較ツール2 まず、JListの代わりにJTableを使用して、カラムの選択と順序の変更を可能にします。 カラムの順序変更のために、JTableをドラッグ&ドロップ可能にします。 選択されたカラムとその順序を取… sato_ken09 tag:qiita.com,2005:PublicArticle/1880534 2024-07-02T13:37:41+09:00 2024-07-31T18:18:45+09:00 https://qiita.com/sato_ken09/items/88ca620f4b1889802a0b java csv比較ツール 比較元と比較先のSJISエンコードされたCSVファイルをSQLiteデータベースにインポートし、その差分をSJISエンコードされたCSVファイルとして出力するSwingベースのアプリケーションです。 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.border.LineBorder; import org.apache.commons.lang3.StringUtils; import org.supercsv.io.CsvListWriter; import org.supercsv.prefs.CsvPreference; import org.supercsv.quote.AlwaysQuoteMode; import org.supercsv.quote.NormalQuoteMode; import com.opencsv.CSVReader; import com.opencsv.CSVReaderBuilder; import com.opencsv.exceptions.CsvValidationException; public class CsvToSQLiteApp extends JFrame { private static final String DB_URL = "jdbc:sqlite:example.db"; private static final int BATCH_SIZE = 1000; private static final int CHUNK_SIZE = 10000; // 1万件ごとに分割するための定数 private JTextField beforeDirPathField; private JTextField afterDirPathField; private JTextField outputCsvPathField; private JCheckBox headerCheckBox; private JCheckBox outPutHeaderCheckBox; private JLabel statusLabel; private JTextArea messageArea; private JProgressBar progressBar; private JPanel columnSelectionPanel; private JButton importButton; private List<JCheckBox> columnCheckBoxes = new ArrayList<>(); // 出力の囲い文字設定用のフィールドを追加 private JTextField outputQuoteField; // 出力ファイル名設定用のフィールドを追加 private JTextField outputFileNameField; private CsvPreference preference = new CsvPreference.Builder('\"', ',', "\r\n") .useQuoteMode(new AlwaysQuoteMode()) .build(); public CsvToSQLiteApp() { setTitle("CSV to SQLite App"); setBounds(0, 100, 620, 780); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new FlowLayout()); setResizable(false); GridBagLayout layout = new GridBagLayout(); GridBagConstraints gbc = new GridBagConstraints(); //比較元ディレクトリパス の設定 JLabel beforeDirPathLabel = new JLabel("比較元ディレクトリパス : "); beforeDirPathField = new JTextField(35); JButton selectBeforeDirButton = new JButton("選択"); selectBeforeDirButton.setPreferredSize(new Dimension(60, 20)); JPanel p = new JPanel(); p.setLayout(layout); p.setPreferredSize(new Dimension(600, 40)); p.setLayout(new FlowLayout(FlowLayout.LEFT)); p.setBorder(new LineBorder(new Color(220, 220, 220), 2, true)); gbc.gridx = 0; gbc.gridy = 0; p.add(beforeDirPathLabel, gbc); gbc.gridx = 1; p.add(beforeDirPathField, gbc); gbc.gridx = 2; p.add(selectBeforeDirButton, gbc); getContentPane().add(p, BorderLayout.PAGE_START); JLabel afterDirPathLabel = new JLabel("比較先ディレクトリパス : "); afterDirPathField = new JTextField(35); JButton selectAfterDirButton = new JButton("選択"); selectAfterDirButton.setPreferredSize(new Dimension(60, 20)); JPanel p2 = new JPanel(); p2.setLayout(layout); p2.setPreferredSize(new Dimension(600, 40)); p2.setLayout(new FlowLayout(FlowLayout.LEFT)); p2.setBorder(new LineBorder(new Color(220, 220, 220), 2, true)); gbc.gridx = 0; gbc.gridy = 0; p2.add(afterDirPathLabel, gbc); gbc.gridx = 1; p2.add(afterDirPathField, gbc); gbc.gridx = 2; p2.add(selectAfterDirButton, gbc); getContentPane().add(p2, BorderLayout.PAGE_START); headerCheckBox = new JCheckBox("取込ファイルの1行目をヘッダー行とする"); headerCheckBox.setSelected(true); JPanel checkP = new JPanel(); checkP.setLayout(layout); checkP.setPreferredSize(new Dimension(600, 40)); checkP.setLayout(new FlowLayout(FlowLayout.LEFT)); checkP.add(headerCheckBox, gbc); getContentPane().add(checkP, BorderLayout.PAGE_START); JLabel outputCsvPathLabel = new JLabel("出力先ディレクトリパス : "); outputCsvPathField = new JTextField(35); JButton diffButton = new JButton("選択"); diffButton.setPreferredSize(new Dimension(60, 20)); JPanel p3 = new JPanel(); p3.setLayout(layout); p3.setPreferredSize(new Dimension(600, 40)); p3.setLayout(new FlowLayout(FlowLayout.LEFT)); p3.setBorder(new LineBorder(new Color(220, 220, 220), 2, true)); gbc.gridx = 0; gbc.gridy = 0; p3.add(outputCsvPathLabel, gbc); gbc.gridx = 1; p3.add(outputCsvPathField, gbc); gbc.gridx = 2; p3.add(diffButton, gbc); getContentPane().add(p3, BorderLayout.PAGE_START); // 出力の囲い文字設定用のコンポーネントを追加 JLabel outputQuoteLabel = new JLabel("出力の囲い文字:"); outputQuoteField = new JTextField(10); outputQuoteField.setPreferredSize(new Dimension(20, 20)); outputQuoteField.setText("\""); // デフォルト値を設定 JPanel p4 = new JPanel(); p4.setPreferredSize(new Dimension(298, 40)); p4.setLayout(new FlowLayout(FlowLayout.LEFT)); p4.setBorder(new LineBorder(new Color(220, 220, 220), 2, true)); p4.add(outputQuoteLabel, gbc); p4.add(outputQuoteField, gbc); getContentPane().add(p4, BorderLayout.PAGE_START); // 出力ファイル名設定用のコンポーネントを追加 JLabel outputFileNameLabel = new JLabel("出力ファイル名:"); outputFileNameField = new JTextField(15); outputFileNameField.setPreferredSize(new Dimension(20, 20)); outputFileNameField.setText("output"); JPanel p5 = new JPanel(); p5.setPreferredSize(new Dimension(297, 40)); p5.setLayout(new FlowLayout(FlowLayout.LEFT)); p5.setBorder(new LineBorder(new Color(220, 220, 220), 2, true)); p5.add(outputFileNameLabel, gbc); p5.add(outputFileNameField, gbc); getContentPane().add(p5, BorderLayout.PAGE_START); outPutHeaderCheckBox = new JCheckBox("出力ファイルにヘッダーを出力する"); outPutHeaderCheckBox.setSelected(true); JPanel checkP2 = new JPanel(); checkP2.setLayout(layout); checkP2.setPreferredSize(new Dimension(600, 40)); checkP2.setLayout(new FlowLayout(FlowLayout.LEFT)); checkP2.add(outPutHeaderCheckBox, gbc); getContentPane().add(checkP2, BorderLayout.PAGE_START); statusLabel = new JLabel("進捗状況:"); progressBar = new JProgressBar(); progressBar.setStringPainted(true); progressBar.setPreferredSize(new Dimension(580, 20)); JPanel barP = new JPanel(); barP.setPreferredSize(new Dimension(600, 50)); barP.setLayout(new FlowLayout(FlowLayout.LEFT)); barP.add(statusLabel, gbc); barP.add(progressBar, gbc); getContentPane().add(barP, BorderLayout.PAGE_START); JPanel msgP = new JPanel(); msgP.setPreferredSize(new Dimension(600, 200)); msgP.setLayout(new FlowLayout(FlowLayout.LEFT)); messageArea = new JTextArea(580, 50); messageArea.setEditable(false); JLabel msgLabel = new JLabel("メッセージ:"); JScrollPane messageScrollPane = new JScrollPane(messageArea); messageScrollPane.setPreferredSize(new Dimension(580, 150)); msgP.add(msgLabel, gbc); msgP.add(messageScrollPane, gbc); getContentPane().add(msgP, BorderLayout.PAGE_START); importButton = new JButton("実行"); JPanel inputBtnP = new JPanel(); inputBtnP.setLayout(new GridBagLayout()); inputBtnP.setPreferredSize(new Dimension(600, 40)); inputBtnP.setLayout(new FlowLayout(FlowLayout.RIGHT)); inputBtnP.add(importButton, gbc); getContentPane().add(inputBtnP, BorderLayout.PAGE_START); selectBeforeDirButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int result = fileChooser.showOpenDialog(null); if (result == JFileChooser.APPROVE_OPTION) { File selectedDir = fileChooser.getSelectedFile(); beforeDirPathField.setText(selectedDir.getAbsolutePath()); } } }); selectAfterDirButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int result = fileChooser.showOpenDialog(null); if (result == JFileChooser.APPROVE_OPTION) { File selectedDir = fileChooser.getSelectedFile(); afterDirPathField.setText(selectedDir.getAbsolutePath()); } } }); diffButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int result = fileChooser.showOpenDialog(null); if (result == JFileChooser.APPROVE_OPTION) { File selectedDir = fileChooser.getSelectedFile(); outputCsvPathField.setText(selectedDir.getAbsolutePath()); } } }); importButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { String beforeDirPath = beforeDirPathField.getText(); String afterDirPath = afterDirPathField.getText(); String outputCsvPath = outputCsvPathField.getText(); boolean hasHeader = headerCheckBox.isSelected(); String outputQuote = outputQuoteField.getText(); String outputFileName = outputFileNameField.getText(); if (StringUtils.isNotEmpty(outputQuote)) { preference = new CsvPreference.Builder(outputQuote.charAt(0), ',', "\r\n") .useQuoteMode(new AlwaysQuoteMode()) .build(); } else { preference = new CsvPreference.Builder('\"', ',', "\r\n") .useQuoteMode(new NormalQuoteMode()) .build(); } SwingWorker<Void, Integer> worker = new SwingWorker<Void, Integer>() { @Override protected Void doInBackground() throws Exception { updateProgress(0); // データベースファイルの削除 File dbFile = new File("example.db"); if (dbFile.exists()) { dbFile.delete(); } try (Connection conn = DriverManager.getConnection(DB_URL)) { importCsvToTable(conn, beforeDirPath, "before", hasHeader); publish(50); importCsvToTable(conn, afterDirPath, "after", hasHeader); publish(66); createIndexes(conn, "before"); createIndexes(conn, "after"); exportDiffToCsv(conn, "before", "after", outputCsvPath, outputFileName); exportDiffDelToCsv(conn, "after", "before", outputCsvPath, outputFileName); publish(100); // カラムリストの更新 updateColumnSelectionPanel(conn); } catch (SQLException ex) { ex.printStackTrace(); appendMessage("CSVファイルの処理中にエラーが発生しました: " + ex.getMessage()); JOptionPane.showMessageDialog(null, "CSVファイルの処理中にエラーが発生しました: " + ex.getMessage()); } return null; } @Override protected void process(List<Integer> chunks) { int latestProgress = chunks.get(chunks.size() - 1); updateProgress(latestProgress); } @Override protected void done() { appendMessage("処理完了"); } }; worker.execute(); } }); } private void importCsvToTable(Connection conn, String csvDirPath, String tableName, boolean hasHeader) throws SQLException { File folder = new File(csvDirPath); File[] listOfFiles = folder.listFiles((dir, name) -> name.toUpperCase().endsWith(".CSV")); if (listOfFiles == null) { JOptionPane.showMessageDialog(null, "指定されたディレクトリにCSVファイルが見つかりません: " + csvDirPath); return; } conn.setAutoCommit(false); try (Statement stmt = conn.createStatement()) { stmt.execute("DROP TABLE IF EXISTS " + tableName); } for (File file : listOfFiles) { try (CSVReader csvReader = new CSVReaderBuilder(new InputStreamReader(new FileInputStream(file), "MS932")) .withSkipLines(0) .build()) { String[] headers = hasHeader ? csvReader.readNext() : generateDefaultHeaders(csvReader.peek()); if (headers == null) { continue; } while (headers[0].startsWith("#")) { headers = csvReader.readNext(); if (headers == null) { break; } } createTable(conn, tableName, headers); String insertSQL = generateInsertSQL(tableName, headers); try (PreparedStatement pstmt = conn.prepareStatement(insertSQL)) { String[] row; int count = 0; while ((row = csvReader.readNext()) != null) { if (row[0].startsWith("#")) { continue; } for (int i = 0; i < row.length; i++) { pstmt.setString(i + 1, row[i]); } pstmt.addBatch(); if (++count % BATCH_SIZE == 0) { pstmt.executeBatch(); conn.commit(); } } pstmt.executeBatch(); // 残りのバッチを実行 conn.commit(); } appendMessage("CSVファイルをインポートしました: " + file.getName()); } catch (IOException | CsvValidationException | SQLException e) { conn.rollback(); e.printStackTrace(); appendMessage("ファイルの処理中にエラーが発生しました " + file.getName() + ": " + e.getMessage()); } } conn.setAutoCommit(true); } private String[] generateDefaultHeaders(String[] firstRow) { String[] headers = new String[firstRow.length]; for (int i = 0; i < firstRow.length; i++) { headers[i] = "column" + (i + 1); } return headers; } private void createTable(Connection conn, String tableName, String[] headers) throws SQLException { List<String> columns = new ArrayList<>(); for (String header : headers) { columns.add("\"" + header.replaceAll("\"", "\"\"") + "\" TEXT"); } String sql = String.format("CREATE TABLE IF NOT EXISTS %s (%s)", tableName, String.join(",", columns)); try (Statement stmt = conn.createStatement()) { stmt.execute(sql); } } private String generateInsertSQL(String tableName, String[] headers) { String[] columns = Arrays.stream(headers) .map(header -> "\"" + header.replaceAll("\"", "\"\"") + "\"") .toArray(String[]::new); String placeholders = String.join(",", Arrays.stream(headers).map(header -> "?").toArray(String[]::new)); return String.format("INSERT INTO %s (%s) VALUES (%s)", tableName, String.join(",", columns), placeholders); } private void createIndexes(Connection conn, String tableName) throws SQLException { try (Statement stmt = conn.createStatement()) { String createIndexSQL = String.format("CREATE INDEX IF NOT EXISTS idx_%s_all ON %s (%s)", tableName, tableName, String.join(",", getColumnNames(conn, tableName))); stmt.execute(createIndexSQL); } } private String[] getColumnNames(Connection conn, String tableName) throws SQLException { List<String> columnNames = new ArrayList<>(); try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("PRAGMA table_info(" + tableName + ")")) { while (rs.next()) { columnNames.add("\"" + rs.getString("name").replaceAll("\"", "\"\"") + "\""); } } return columnNames.toArray(new String[0]); } private void exportDiffToCsv(Connection conn, String tableA, String tableB, String outputDirPath, String outputFileName) throws SQLException, IOException { String sql = String.format("SELECT * FROM %s EXCEPT SELECT * FROM %s", tableA, tableB); CsvListWriter writer = null; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); int fileCount = 1; int rowCount = 0; File outputDir = new File(outputDirPath); if (!outputDir.exists()) { outputDir.mkdirs(); } String outputFilePath = String.format("%s/%s_比較先変更行_%03d.csv", outputDirPath, outputFileName, fileCount); writer = new CsvListWriter( new OutputStreamWriter(new FileOutputStream(outputFilePath), "MS932"), preference); //出力ファイルにヘッダーを出力するにチェックがある場合は実行する if (outPutHeaderCheckBox.isSelected()) { String[] headerRow = new String[columnCount]; for (int i = 1; i <= columnCount; i++) { headerRow[i - 1] = metaData.getColumnName(i); } writer.write(headerRow); } while (rs.next()) { String[] row = new String[columnCount]; for (int i = 1; i <= columnCount; i++) { row[i - 1] = rs.getString(i); } writer.write(row); rowCount++; if (rowCount % CHUNK_SIZE == 0) { writer.close(); fileCount++; outputFilePath = String.format("%s/%s_比較先変更行_%03d.csv", outputDirPath, outputFileName, fileCount); writer = new CsvListWriter( new OutputStreamWriter(new FileOutputStream(outputFilePath), "MS932"), preference); } } appendMessage("差分をCSVにエクスポートしました: " + outputDirPath); } finally { if (Objects.nonNull(writer)) { writer.close(); } } } private void exportDiffDelToCsv(Connection conn, String tableA, String tableB, String outputDirPath, String outputFileName) throws SQLException, IOException { String sql = String.format("SELECT * FROM %s EXCEPT SELECT * FROM %s", tableA, tableB); CsvListWriter writer = null; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql)) { ResultSetMetaData metaData = rs.getMetaData(); int columnCount = metaData.getColumnCount(); int fileCount = 1; int rowCount = 0; File outputDir = new File(outputDirPath); if (!outputDir.exists()) { outputDir.mkdirs(); } String outputFilePath = String.format("%s/%s_比較元変更行_%03d.csv", outputDirPath, outputFileName, fileCount); writer = new CsvListWriter( new OutputStreamWriter(new FileOutputStream(outputFilePath), "MS932"), preference); //出力ファイルにヘッダーを出力するにチェックがある場合は実行する if (outPutHeaderCheckBox.isSelected()) { String[] headerRow = new String[columnCount]; for (int i = 1; i <= columnCount; i++) { headerRow[i - 1] = metaData.getColumnName(i); } writer.write(headerRow); } while (rs.next()) { String[] row = new String[columnCount]; for (int i = 1; i <= columnCount; i++) { row[i - 1] = rs.getString(i); } writer.write(row); rowCount++; if (rowCount % CHUNK_SIZE == 0) { writer.close(); fileCount++; outputFilePath = String.format("%s/%s_比較元変更行_%03d.csv", outputDirPath, outputFileName, fileCount); writer = new CsvListWriter( new OutputStreamWriter(new FileOutputStream(outputFilePath), "MS932"), preference); } } appendMessage("差分をCSVにエクスポートしました: " + outputDirPath); } finally { if (Objects.nonNull(writer)) { writer.close(); } } } private void updateColumnSelectionPanel(Connection conn) throws SQLException { columnSelectionPanel.removeAll(); columnCheckBoxes.clear(); String query = "PRAGMA table_info(before)"; try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query)) { GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets(5, 5, 5, 5); gbc.gridx = 0; int row = 0; while (rs.next()) { String columnName = rs.getString("name"); JCheckBox checkBox = new JCheckBox(columnName); columnCheckBoxes.add(checkBox); gbc.gridy = row++; columnSelectionPanel.add(checkBox, gbc); } } columnSelectionPanel.revalidate(); columnSelectionPanel.repaint(); } private synchronized void updateProgress(int percent) { progressBar.setValue(percent); } private synchronized void appendMessage(String message) { messageArea.append(message + "\n"); } public static void main(String[] args) { SwingUtilities.invokeLater(() -> new CsvToSQLiteApp().setVisible(true)); } } … sato_ken09