Skip to content

Instantly share code, notes, and snippets.

@kujirahand
Created February 3, 2026 03:14
Show Gist options
  • Select an option

  • Save kujirahand/9b57f10821d436d60e7371f4cdd5f2db to your computer and use it in GitHub Desktop.

Select an option

Save kujirahand/9b57f10821d436d60e7371f4cdd5f2db to your computer and use it in GitHub Desktop.
住所で郵便番号データをソート
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
// 対象CSVファイルを開いてバッファ付きリーダーで巻き取る --- (*1)
let file = File::open("utf_ken_all.csv")?;
let reader = BufReader::new(file);
// 住所カナや郵便番号・住所を一時保存する構造体を蓄積 --- (*2)
let mut entries = Vec::new();
// 1行ずつ読み込んでCSVフィールドを抽出 --- (*3)
for line in reader.lines() {
let line = line?;
if line.is_empty() {
continue;
}
// フィールドを分割してトリムし、必要な情報を抽出 --- (*4)
let fields: Vec<String> = line
.split(',')
.map(|s| s.trim_matches('"').to_string())
.collect();
// 住所カタカナ(5列目と6列目)、郵便番号(3列目)、住所(8列目と9列目)を取得 --- (*5)
let kana_key = format!("{}{}", fields.get(4).map(String::as_str).unwrap_or(""), fields.get(5).map(String::as_str).unwrap_or(""));
let postal = fields.get(2).map(String::as_str).unwrap_or("").to_string();
let address = format!("{}{}", fields.get(7).map(String::as_str).unwrap_or(""), fields.get(8).map(String::as_str).unwrap_or(""));
// 抽出した情報をタプルとしてベクタに追加 --- (*6)
entries.push((kana_key, postal, address));
}
// 住所カナをキーに昇順ソートし、先頭5件を出力 --- (*7)
entries.sort_by(|a, b| a.0.cmp(&b.0));
for (_, postal, address) in entries.iter().take(5) {
println!("{} {}", postal, address);
}
Ok(())
}
@kujirahand
Copy link
Author

メモリ使用量に配慮したプログラム

// メモリを抑えつつ、住所カナ(5+6列目)昇順の先頭5件だけを保持して出力
// ポイント: 全件を保持・ソートせず、ヒープで上位5件のみを管理する
use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};

fn main() -> std::io::Result<()> {
    // 入力CSVをバッファ付きで逐次読み込み(メモリを節約)
    let file = File::open("utf_ken_all.csv")?;
    let reader = BufReader::new(file);

    // 先頭5件だけを保持する最大ヒープ(キーは結合カナ)
    // ヒープのサイズを5に制限し、それより大きくなったら最大要素を捨てる
    let mut heap: BinaryHeap<Entry> = BinaryHeap::new();

    for line in reader.lines() {
        let line = line?;
        if line.is_empty() {
            continue;
        }

        // カンマ分割し、不要なダブルクォートを取り除く
        let fields: Vec<&str> = line
            .split(',')
            .map(|s| s.trim_matches('"'))
            .collect();

        if fields.len() < 9 {
            continue; // 想定より列が少ない行はスキップ
        }

        // キー(住所カナ)と出力項目(郵便番号+住所)を組み立て
        let kana_key = format!("{}{}", fields[4], fields[5]);
        let postal = fields[2].to_string();
        let address = format!("{}{}", fields[7], fields[8]);

        let entry = Entry { kana_key, postal, address };

        // ヒープへ投入し、サイズが6以上なら最大要素を捨てて5件に抑える
        heap.push(entry);
        if heap.len() > 5 {
            heap.pop(); // 最大ヒープなので「大きい」要素を捨てる=上位5件を維持
        }
    }

    // ヒープから取り出した5件を昇順に整列させて出力
    // into_sorted_vec() はヒープ順(降順)から昇順ベクタを生成してくれる
    let mut top = heap.into_sorted_vec();
    let mut stdout = std::io::BufWriter::new(std::io::stdout());
    for entry in top.drain(..) {
        writeln!(stdout, "{} {}", entry.postal, entry.address)?;
    }

    Ok(())
}

#[derive(Debug, Eq)]
struct Entry {
    kana_key: String, // ヒープのソートキー(住所カナ結合)
    postal: String,   // 郵便番号
    address: String,  // 住所(8+9列目)
}

impl Ord for Entry {
    fn cmp(&self, other: &Self) -> Ordering {
        // 逆順で保持したいので通常比較を反転
        other.kana_key.cmp(&self.kana_key).then_with(|| other.postal.cmp(&self.postal))
    }
}

impl PartialOrd for Entry {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl PartialEq for Entry {
    fn eq(&self, other: &Self) -> bool {
        self.kana_key == other.kana_key && self.postal == other.postal && self.address == other.address
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment