92677


RustでDIMACS CNF ファイルを読み込みたいです。

Rubyで書くと以下のようなコードになるのですがRustだとどう書けばいいでしょうか？  
特に、Rubyのscanに当たる関数が見つかってないです。

[code]

    #Ruby code
    
    def read_dimacs_cnf_file(file_name)
        cnf=[]
        clause=[]
        open(file_name,"r").readlines.reject{|line| line=~/^c/ || line=~/^p/}.join().scan(/-?\d+/).collect{|literal| literal.to_i}.each{|literal|
            if literal==0
            then
                cnf<<clause
                clause=[]
            else
                clause<<literal
            end
        }
        cnf
    end
    
[/code]

入力例

[code]

    c
    c comment
    c
    p cnf 3 3
    1 2 0
    2 -3 0
    3 0
    
[/code]

出力例

[code]

    [[1,2],[2,-3],[3]]
    
[/code]

RustのコードはFileを読み込むところまでしか書けてないです。

[code]

    use std::error::Error;
    use std::fs::File;
    use std::io::prelude::*;
    use std::path::Path;
    
    fn read_dimacs_cnf_file(file_name:&str)->String{
        let path=Path::new(file_name);
        let mut file=match File::open(&path){
            Err(msg) => panic!("couldn't open file. {}",msg),
            Ok(file) => file,
        };
    
        let mut s=String::new();
        match file.read_to_string(&mut s){
            Err(msg) => panic!("couldn't read file. {}",msg),
            Ok(_) => return s,
        };
    }
    
    fn main() {
        println!("{}",read_dimacs_cnf_file("test.cnf"));
    }
    
[/code]


CC BY-SA 4.0
2022-12-04T08:29:51.260
92570
2023-11-14T06:56:15.603
18637
question
rust
DIMACS CNF 形式のファイルを読み込みたい


> 特に、Rubyのscanに当たる関数が見つかってないです。

Rubyの`scan`などで`/-?\d+/`といった正規表現が使われていますが、Rustの標準ライブラリー（`std`クレート）には正規表現の機能がありません。正規表現を使う場合は、[`regex`クレート](https://crates.io/crates/regex)を使う必要があります。

**Cargo.toml**

[code]

    [dependencies]
    regex = "1.7.0"
    
[/code]

`regex`クレートの`captures_iter`メソッドを使うと、`scan`のようにマッチした部分文字列を取り出すことができます。

[code]

    use std::{
        fs::File,
        io::{prelude::*, BufReader},
    };
    
    use regex::Regex;
    
    fn read_dimacs_cnf_file(file_name: &str) -> Vec<Vec<i32>> {
        let file = File::open(file_name).expect("couldn't open file.");
        let file = BufReader::new(file);
    
        let re = Regex::new(r"-?\d+").unwrap();
        let mut clause = Vec::new();
        let mut cnf = Vec::new();
    
        // 1行ずつ読み込む
        for line in file.lines() {
            let line = line.expect("couldn't read file.");
    
            // c または p で始まる行は無視する
            if line.starts_with("c") || line.starts_with("p") {
                continue;
            }
    
            // 行の中で、マッチした部分文字列を1つずつ処理する
            for token in re.captures_iter(&line) {
                // 部分文字列を整数に変換する
                let literal = token[0].parse::<i32>().expect("couldn't parse number.");
    
                if literal == 0 {
                    // 0なら、clauseをcnfに追加して、clauseを空にする
                    cnf.push(clause);
                    clause = Vec::new();
                } else {
                    // 0以外なら、clauseに追加する
                    clause.push(literal);
                }
            }
        }
    
        cnf
    }
    
    fn main() {
        println!("{:?}", read_dimacs_cnf_file("test.cnf"));
    }
    
[/code]

なお、このデーター形式ですと数値が空白文字で区切られていますので、正規表現を使わずに`std`の`str::split_whitespace`メソッドを使うこともできます。以下のように2行書き換えると`regex`クレートが不要になります。

[code]

            // 行の中で、空白文字で区切られた部分文字列を1つずつ処理する
            for token in line.split_whitespace() {
                // 部分文字列を整数に変換する
                let literal = token.trim().parse::<i32>().expect("couldn't parse number.");
    
[/code]


CC BY-SA 4.0
2022-12-10T08:13:34.633
92677
2022-12-10T08:13:34.633
14101
92570
answer


rustにはdimacsというクレートがあってDIMACS CNFファイルをパースできるみたいです。

<https://docs.rs/dimacs/latest/dimacs/index.html>


CC BY-SA 4.0
2023-11-14T06:56:15.603
96777
2023-11-14T06:56:15.603
18637
92570
answer