我试图编写一个Rust程序,在stdin
上获取一个单独的文件名列表。
在Windows上,我可以从cmd
窗口调用它,例如:
dir /b /s | findstr .*,v$ | rust-prog -n
在Unix上我会使用类似的东西:
find . -name '*,v' -print0 | rust-prog -0
我无法将stdin
上收到的内容转换为std::path::Path
可以使用的内容。据我所知,要获得可在Windows或Unix上编译的内容,我将需要使用条件编译,并std::os::windows::ffi
或std::os::unix::ffi
。
此外,在Windows上我似乎需要使用kernel32::MultiByteToWideChar
使用当前代码页来创建std::os::windows::ffi::OsStrExt
可用的内容。
有更简单的方法吗?我所建议的甚至看起来是否可行?
例如,将字符串转换为路径很容易,因此我尝试使用stdin
的字符串处理函数:
use std::io::{self, Read};
fn main() {
let mut buffer = String::new();
match io::stdin().read_line(&mut buffer) {
Ok(n) => println!("{}", buffer),
Err(error) => println!("error: {}", error)
}
}
在Windows上,如果我的目录中包含一个名为¿.txt
的文件(即0xbf)。并将名称输入stdin
。我得到:error: stream did not contain valid UTF-8
。
答案 0 :(得分:1)
这是Windows的合理外观版本。使用win32api函数将控制台提供的字符串转换为宽字符串,然后使用OsString::from_wide
将其包装在OsString中。
我不相信它还使用了正确的代码页。 dir
似乎使用OEM代码页,所以也许这应该是默认代码。在控制台中输入代码页和输出代码页之间也有区别。
在我的Cargo.toml
[dependencies]
winapi = "0.2"
kernel32-sys = "0.2.2"
根据问题读取在Windows上通过stdin传输的文件名列表的代码。
extern crate kernel32;
extern crate winapi;
use std::io::{self, Read};
use std::ptr;
use std::fs::metadata;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
/// Convert windows console input to wide string that can
/// be used by OS functions
fn wide_from_console_string(bytes: &[u8]) -> Vec<u16> {
assert!(bytes.len() < std::i32::MAX as usize);
let mut wide;
let mut len;
unsafe {
let cp = kernel32::GetConsoleCP();
len = kernel32::MultiByteToWideChar(cp, 0, bytes.as_ptr() as *const i8, bytes.len() as i32, ptr::null_mut(), 0);
wide = Vec::with_capacity(len as usize);
len = kernel32::MultiByteToWideChar(cp, 0, bytes.as_ptr() as *const i8, bytes.len() as i32, wide.as_mut_ptr(), len);
wide.set_len(len as usize);
}
wide
}
/// Extract paths from a list supplied as Cr LF
/// separated wide string
/// Would use a generic split on substring if it existed
fn paths_from_wide(wide: &[u16]) -> Vec<OsString> {
let mut r = Vec::new();
let mut start = 0;
let mut i = start;
let len = wide.len() - 1;
while i < len {
if wide[i] == 13 && wide[i + 1] == 10 {
if i > start {
r.push(OsString::from_wide(&wide[start..i]));
}
start = i + 2;
i = i + 2;
} else {
i = i + 1;
}
}
if i > start {
r.push(OsString::from_wide(&wide[start..i]));
}
r
}
fn main() {
let mut bytes = Vec::new();
if let Ok(_) = io::stdin().read_to_end(&mut bytes) {
let pathlist = wide_from_console_string(&bytes[..]);
let paths = paths_from_wide(&pathlist[..]);
for path in paths {
match metadata(&path) {
Ok(stat) => println!("{:?} is_file: {}", &path, stat.is_file()),
Err(e) => println!("Error: {:?} for {:?}", e, &path)
}
}
}
}