fn main() {
let source:&str = r#"<!DOCTYPE html> <html lang="ja"> <head> <title> HTML in the code </title> </head> <body> <div id="mymain"> <p> first line </p> <p> second <br/> line </p> <p> third line </p> <img src="data.jpg" /> </div> <div id="myfooter"> <p>copyright</p> </div> </body> </html "#;
//////////////////////////
// HTMLの解析
let myparse:scraper::Html = scraper::Html::parse_document(&source);
let selector:scraper::Selector = scraper::Selector::parse("*").unwrap();
let parsed:scraper::ElementRef = myparse.select(&selector).next().unwrap();
println!("*************************");
traverse_element(&parsed,1);
}
/// @brief 解析済みのHTMLを再帰的に走査して内容を表示 /// @param [in] elementref 解析済みのエレメント /// @param [in] depth 再帰の階層 /// @return なし fn traverse_element(elementref: &scraper::ElementRef, depth: usize) { let indent:String = " ".repeat(depth*3);// 階層に合わせて左側に挿入するスペース let element:&scraper::node::Element = elementref.value(); let tag_name:&str = element.name(); print!("{}",depth); // 階層の表示 print!("{} {}",indent,tag_name); // タグ名表示 // 属性取得・保存 for attr in element.attrs(){ let attr_name =&attr.0.to_string(); let attr_value =&attr.1.to_string(); print!(" {} = {}", &attr_name, &attr_value ); } println!(""); // 子要素を再帰的に辿る for child in elementref.children() { if let Some(child_element) = child.value().as_element() { // タグの場合 // 子要素を走査 let c_elem_ref:scraper::ElementRef = scraper::ElementRef::wrap(child).unwrap(); traverse_element(&c_elem_ref, depth + 1); } else if let Some(text) = child.value().as_text() { // コンテンツの場合 let contents:&str = text.trim(); if !contents.is_empty() { print!("{}",depth); // コンテンツを表示 println!("[[[[[[[[[[[[[[[[[[{}]]]]]]]]]]]]]]]]]]]]]", contents); } } } }
1 html lang = ja
2 head
3 title
3[[[[[[[[[[[[[[[[[[HTML in the code]]]]]]]]]]]]]]]]]]]]]
2 body
3 div id = mymain
4 p
4[[[[[[[[[[[[[[[[[[first line]]]]]]]]]]]]]]]]]]]]]
4 p
4[[[[[[[[[[[[[[[[[[second]]]]]]]]]]]]]]]]]]]]]
5 br
4[[[[[[[[[[[[[[[[[[line]]]]]]]]]]]]]]]]]]]]]
4 p
4[[[[[[[[[[[[[[[[[[third
line]]]]]]]]]]]]]]]]]]]]]
4 img src = data.jpg
3 div id = myfooter
4 p
4[[[[[[[[[[[[[[[[[[copyright]]]]]]]]]]]]]]]]]]]]]
IntelliJ IDEAでJSoupを使ってみる。
全くやったことがないので導入だけのメモになる。
コードは以下の通り。
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; fun main(args: Array<String>) { val url = "https://suzulang.com/" val doc = Jsoup.connect(url).get(); println(doc.title()); // タイトル取得 // divの内容をリストで取得 val divs = doc.select("div"); for(div in divs){ // div に子要素があればその内容をループ if( div.children().size > 0 ){ // pの一覧を取得 val paragraphs = div.children().select("p"); for(p in paragraphs){ println(p.text()); // pの内容を表示 } } } }
以下からダウンロード

展開したら、[File]→[Project Structure...]を選択
Dependenciesの中の+をクリックし、JARs or Directories...を選択
jsoup-1.15.4.jarファイルへのフルパスを指定。ディレクトリまでしか指定しないとjarファイルが表示されないので入力欄にファイル名まで全部入力するといい。
lxmlを使うとデータをツリー構造に格納してくれる。
import lxml.html # HTMLのテキスト text = """<html> <head><title>タイトル</title> <body> <p> hello world </p> <p> 二行目 </p> <div> 子要素 </div> </body> </html> """ ret = lxml.html.fromstring( text ) for itr in ret: # forでイテレーションする print(itr.tag) # タグへアクセス if len(list(itr)): # 子要素があるかどうかはlistの長さを調べる print("{") for i in itr: print(i.tag ,"[" , i.text , "]" ) print("}")
lxml.html.parseはURLを渡せるのだが、
どうやらHTTPSに対応していないらしい。
urllib.requestを使ってHTMLを取得し、それをfromstringへ入力する。
# lxmlはhttpsに対応していない。 # html.parse( /*ここに入れていいのはhttpのURLだけ*/ ) # urllib.request.urlopenを使ってhttpsからテキストを取得してそれを入力する # from urllib import urlopen # Python2だとurllib2らしい import urllib.request urldata = urllib.request.urlopen('https://suzulang.com/') text = urldata.read() print( text )
HTMLParserでHTMLをパースする。
HTMLParserクラスは、このクラスを継承して使用する。
HTMLParser.feedを呼び出すと、タグなどが見つかった際にオーバーライドしたhandle_starttagメソッドが呼ばれるので、その度に必要な処理を記述する。
注意
公式ドキュメント
https://docs.python.org/3/library/html.parser.html
にあるコードだとHTML内に改行が見つかるたびにhandle_dataが呼び出されてしまうので、\n単体が見つかった場合は何もしないように処理を追加。
from html.parser import HTMLParser ##################################### # HTMLのテキスト text = """<html> <body> <p> hello world </p> </body> </html> """
##################################### #https://docs.python.org/3/library/html.parser.html # HTMLParserを継承したMyHTMLParserを作成 class MyHTMLParser(HTMLParser): # HTMLParser.feed を呼び出すと、以下の各関数がパース中に呼び出される def handle_starttag(self, tag, attrs): print("Encountered a start tag:", tag) def handle_endtag(self, tag): print("Encountered an end tag :", tag) def handle_data(self, data): # タグの後の改行でもhandle_dataが呼び出されてしまうため # 改行単体の時はひとまず抜ける if data == '\n': return print("Encountered some data :", data)
##################################### # パース処理 ps = MyHTMLParser() # パーサー作成 ps.feed(text) # パース実行
OpenGLのウィンドウ生成。
[package]
name = "testglutin"
version = "0.1.0"
edition = "2021"
[dependencies]
glutin = "0.26.0"
gl="0.14.0"
fn main() { let event_loop = glutin::event_loop::EventLoop::new(); let window = glutin::window::WindowBuilder::new().with_title("Rust glutin OpenGL");
// GLのコンテキストを作成 // この gl_context は ContextBuilder<NotCurrent,Window> 型 let gl_context = glutin::ContextBuilder::new() .with_gl(glutin::GlRequest::Specific(glutin::Api::OpenGl, (3, 3))) .build_windowed(window, &event_loop) .expect("Cannot create context"); // Rustにはシャドーイングがあるので、同じ名前の別変数を同じスコープ内に定義できる。 // この gl_context は ContextBuilder<PossibleCurrent,Window> 型 // 以降、gl_currentは以前の型の意味で用いることはできない let gl_context = unsafe { gl_context .make_current() .expect("Failed to make context current") };
// OpenGLの各関数を初期化 // これでgl::Viewportをはじめとする各関数の関数ポインタにアドレスが設定され、呼び出せるようになる。 gl::load_with(|symbol| gl_context.get_proc_address(symbol) as *const _); // event_loopを開始する event_loop.run(move |event, _, control_flow| { // Pollを指定するとループが走り続ける // Waitを指定するとイベントが発生したときだけループが動く。 *control_flow = glutin::event_loop::ControlFlow::Wait; match event { glutin::event::Event::WindowEvent { event, .. } => match event {
//////////////////////////////////////////////////////////////////// // ウィンドウを閉じる glutin::event::WindowEvent::CloseRequested => { // ウィンドウが閉じられた場合、event_loopを停止する *control_flow = glutin::event_loop::ControlFlow::Exit; },
//////////////////////////////////////////////////////////////////// // ウィンドウのサイズを変更 glutin::event::WindowEvent::Resized(new_size) => { // ビューポート再設定 unsafe { gl::Viewport(0, 0, new_size.width as i32, new_size.height as i32); } },
//////////////////////////////////////////////////////////////////// _ => (), }, _ => (), }
// 描画 unsafe { gl::Clear(gl::COLOR_BUFFER_BIT); gl::ClearColor(0.3, 0.3, 0.5, 1.0); } // スワップバッファ gl_context.swap_buffers().unwrap();
}); }
カーニングはアルファベットを描画する際に例えばVとAの間を狭く描画するような描画位置調整のことで、GT_Get_Kerningで文字の位置の差を取得する。
#include <iostream> #include <vector> #include <ft2build.h> #include FT_FREETYPE_H #pragma warning(disable:4996) #pragma comment(lib,"freetype.lib") void pnmP2_Write( const char* const fname, const int width, const int height, const unsigned char* const p); void draw( const int width, const int height, unsigned char* p, const int charw, const int charh, const int ox, const int oy, const unsigned char* charp ); int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; FT_Face face; // handle to face object // フォントファイル読み込み error = FT_New_Face( library, "C:\\Windows\\Fonts\\meiryo.ttc", 0, &face ); //文字コード指定 error = FT_Select_Charmap( face, // target face object FT_ENCODING_UNICODE // エンコード指定 ); if (error == FT_Err_Unknown_File_Format) return -1; else if (error) return -1; //この二つの値でフォントサイズ調整 FT_F26Dot6 fontsize = 32 * 64; FT_UInt CHAR_RESOLUTION = 300; error = FT_Set_Char_Size( face, // handle to face object 0, // char_width in 1/64th of points fontsize, // char_height in 1/64th of points CHAR_RESOLUTION, // horizontal device resolution CHAR_RESOLUTION); // vertical device resolution // 出力画像のメモリ確保 const int iw = 500; const int ih = 300; std::vector<unsigned char> image(iw*ih,0);
// カーニングのために直前の文字を保存しておく FT_UInt previous_glyph = 0;
// 描画位置 int posx = 0; int posy = 200; for (size_t i = 0; i < 4; i++) { // 文字の取得 FT_ULong character = U"VAOX"[i]; FT_UInt char_index = FT_Get_Char_Index(face, character);
// カーニング
if(previous_glyph && true/*カーニングしないならfalse*/) { FT_Vector kerning_delta; // 文字のずれ量を保存 // 文字のシフト量を取得 FT_Get_Kerning( face, previous_glyph, char_index, FT_KERNING_DEFAULT, &kerning_delta ); posx += ( kerning_delta.x >> 6 );// 文字のシフト } // 直前に使ったグリフを更新 previous_glyph = char_index;
// グリフ(字の形状)読込
error = FT_Load_Glyph(face, char_index, FT_LOAD_RENDER);
if (error)
return -1; // ignore errors
// 文字を画像化
FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
// 出力画像に文字を書き込み
draw(
//出力先データ
iw, ih, image.data(),
// 文字の画像
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
posx + face->glyph->bitmap_left,
posy - face->glyph->bitmap_top,
face->glyph->bitmap.buffer
);
posx += (face->glyph->advance.x >> 6);
posy -= (face->glyph->advance.y >> 6);
}
pnmP2_Write(// ファイル保存
"C:\\MyData\\freetypetest.pgm",
iw,
ih,
image.data()
);
// FreeType2の解放
FT_Done_Face(face);
FT_Done_FreeType(library);
}
//! @brief Portable Gray map
//! @param [in] fname ファイル名
//! @param [in] width 画像の幅
//! @param [in] height 画像の高さ
//! @param [in] p 画像のメモリへのアドレス
//! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む
void pnmP2_Write(
const char* const fname,
const int width,
const int height,
const unsigned char* const p) { // PPM ASCII
FILE* fp = fopen(fname, "wb");
fprintf(fp, "P2\n%d %d\n%d\n", width, height, 255);
size_t k = 0;
for (size_t i = 0; i < (size_t)height; i++) {
for (size_t j = 0; j < (size_t)width; j++) {
fprintf(fp, "%d ", p[k]);
k++;
}
fprintf(fp, "\n");
}
fclose(fp);
}
//! @brief 出力画像へ文字を書き込む
//! @param [in] width 出力先のサイズ
//! @param [in] height 出力先のサイズ
//! @param [out] p 出力先
//! @param [in] charw 文字画像のサイズ
//! @param [in] charh 文字画像のサイズ
//! @param [in] ox 描画始点
//! @param [in] oy 描画始点
//! @param [in] charp 文字画像
void draw(
const int width,
const int height,
unsigned char* p,
const int charw,
const int charh,
const int ox,
const int oy,
const unsigned char* charp
) {
for (int cx = 0; cx < charw; cx++) {
for (int cy = 0; cy < charh; cy++) {
int x = ox + cx;
int y = oy + cy;
if (x < 0 || x >= width)continue;
if (y < 0 || y >= height)continue;
int ipos = y * width + x;
int cpos = cy * charw + cx;
int c = (int)(p[ipos]) + (int)(charp[cpos]);
c = std::min(c, 255);
p[ipos] = c;
}
}
}
#include <iostream> #include <vector> #include <ft2build.h> #include FT_FREETYPE_H #pragma warning(disable:4996) #pragma comment(lib,"freetype.lib") void pnmP2_Write( const char* const fname, const int width, const int height, const unsigned char* const p); void draw( const int width, const int height, unsigned char* p, const int charw, const int charh, const int ox, const int oy, const unsigned char* charp ); int main() { FT_Library library; // handle to library FT_Error error; error = FT_Init_FreeType(&library); if (error) return -1; FT_Face face; // handle to face object // フォントファイル読み込み error = FT_New_Face( library, "C:\\Windows\\Fonts\\meiryo.ttc", 0, &face ); //文字コード指定 error = FT_Select_Charmap( face, // target face object FT_ENCODING_UNICODE // エンコード指定 ); if (error == FT_Err_Unknown_File_Format) return -1; else if (error) return -1; //この二つの値でフォントサイズ調整 FT_F26Dot6 fontsize = 32 * 64; FT_UInt CHAR_RESOLUTION = 300; error = FT_Set_Char_Size( face, // handle to face object 0, // char_width in 1/64th of points fontsize, // char_height in 1/64th of points CHAR_RESOLUTION, // horizontal device resolution CHAR_RESOLUTION); // vertical device resolution // 1 Unit == 1/64 なので、×64 して200 pixel になる FT_F26Dot6 shiftx = 200 * 64; FT_F26Dot6 shifty = 0; FT_Vector mv; mv.x = shiftx; mv.y = 0; FT_Set_Transform(face, 0, &mv); // 出力画像のメモリ確保 const int iw = 1000; const int ih = 1000; std::vector<unsigned char> image(iw*ih,0); // 描画位置 int posx = 0; int posy = 200; for (size_t i = 0; i < 3; i++) { // 文字の取得 FT_ULong character = U"あいう"[i]; FT_UInt char_index = FT_Get_Char_Index(face, character); // グリフ(字の形状)読込 error = FT_Load_Glyph(face, char_index, FT_LOAD_RENDER); if (error) return -1; // ignore errors // 文字を画像化 FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); // 出力画像に文字を書き込み draw( //出力先データ iw, ih, image.data(), // 文字の画像 face->glyph->bitmap.width, face->glyph->bitmap.rows, posx + face->glyph->bitmap_left, posy - face->glyph->bitmap_top, face->glyph->bitmap.buffer ); posx += (face->glyph->advance.x >> 6); posy -= (face->glyph->advance.y >> 6); } pnmP2_Write(// ファイル保存 "C:\\MyData\\freetypetest.pgm", iw, ih, image.data() ); // FreeType2の解放 FT_Done_Face(face); FT_Done_FreeType(library); } //! @brief Portable Gray map //! @param [in] fname ファイル名 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP2_Write( const char* const fname, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "wb"); fprintf(fp, "P2\n%d %d\n%d\n", width, height, 255); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d ", p[k]); k++; } fprintf(fp, "\n"); } fclose(fp); } //! @brief 出力画像へ文字を書き込む //! @param [in] width 出力先のサイズ //! @param [in] height 出力先のサイズ //! @param [out] p 出力先 //! @param [in] charw 文字画像のサイズ //! @param [in] charh 文字画像のサイズ //! @param [in] ox 描画始点 //! @param [in] oy 描画始点 //! @param [in] charp 文字画像 void draw( const int width, const int height, unsigned char* p, const int charw, const int charh, const int ox, const int oy, const unsigned char* charp ) { for (int cx = 0; cx < charw; cx++) { for (int cy = 0; cy < charh; cy++) { int x = ox + cx; int y = oy + cy; if (x < 0 || x >= width)continue; if (y < 0 || y >= height)continue; int ipos = y * width + x; int cpos = cy * charw + cx; int c = (int)(p[ipos]) + (int)(charp[cpos]); c = std::min(c, 255); p[ipos] = c; } } }
[package]
name = "winittest"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
glfw = "0.51.0"
gl="0.14.0"
use glfw::{Action, Context, Key, ffi::glfwTerminate}; fn main() { let mut glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap(); //////////////////////////////////////////////// //////////////////////////////////////////////// let (mut window, events) = glfw.create_window(300, 300, "Hello this is window", glfw::WindowMode::Windowed) .expect("Failed to create GLFW window."); window.make_current(); //////////////////////////////////////////////// //////////////////////////////////////////////// gl::load_with(|s| glfw.get_proc_address_raw(s)); gl::Viewport::load_with(|s| glfw.get_proc_address_raw(s)); //////////////////////////////////////////////// //////////////////////////////////////////////// while window.should_close() == false{ let (width,height)=window.get_framebuffer_size(); unsafe{ gl::Viewport(0,0,width,height); gl::ClearColor(0.5,0.5,0.5,1.0); gl::Clear(gl::COLOR_BUFFER_BIT); } window.swap_buffers(); glfw.wait_events(); } }
なお、
と聞かれた場合、Rustが中でcmake.exeを読んでいるので、cmake.exeへのパスを通しておく必要がある。
Winitはクロスプラットフォームの、Rustでウィンドウを生成するクレート。
ただしこれはウィンドウを生成する機能しか持っておらず、ボタンなどを設置する機能は持っていない。OpenGLもそのままでは使えずWindowsであればwglを使わないといけない。
[package]
name = "winittest"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
winit = "0.28.2"
Rust文法がまだよくわかっていなのでコードの整頓にとどめる。
use winit::{ event::{ Event, WindowEvent, DeviceId, ElementState }, event_loop::{ ControlFlow, EventLoop }, window::WindowBuilder, platform::windows::WindowExtWindows, // hwndを使うなら必要
dpi::PhysicalPosition, }; // https://github.com/rust-windowing/winit fn main() { // イベントループ let event_loop = EventLoop::new(); // ウィンドウ生成 let window = WindowBuilder::new() .with_inner_size(winit::dpi::LogicalSize::new(500, 300)) .with_title("winit") .build(&event_loop).unwrap(); // ウィンドウハンドルを取得 // let hwnd:winit::platform::windows::HWND = (window.hwnd() as isize); event_loop.run(move |event, _, control_flow| { *control_flow = ControlFlow::Wait; let mut pos: PhysicalPosition<f64>; // マウス座標の格納先 match event { /////////////////////////////////////////////// /////////////////////////////////////////////// // 条件 Event::WindowEvent { event: WindowEvent::CloseRequested, window_id, } if window_id == window.id() => // 処理 { *control_flow = ControlFlow::Exit } , /////////////////////////////////////////////// /////////////////////////////////////////////// // 条件 マウス移動 Event::WindowEvent { event:WindowEvent::CursorMoved { position:pos, .. }, window_id, } if window_id == window.id() => // 処理 { println!("{} {}",pos.x,pos.y); }, /////////////////////////////////////////////// /////////////////////////////////////////////// // 条件 左マウスボタン押下 Event::WindowEvent{ event: WindowEvent::MouseInput{ state:ElementState::Pressed, button:winit::event::MouseButton::Left, .. // 「..」で省略できる }, window_id, } if window_id == window.id() => // 処理 { println!("left pushed"); }, /////////////////////////////////////////////// /////////////////////////////////////////////// // 条件 右マウスボタン押下 Event::WindowEvent{ event: WindowEvent::KeyboardInput{ input:winit::event::KeyboardInput{ state:ElementState::Pressed, virtual_keycode:Some(winit::event::VirtualKeyCode::A), .. }, .. // 「..」で省略できる }, window_id, } if window_id == window.id() => // 処理 { println!("A pushed") }, /////////////////////////////////////////////// /////////////////////////////////////////////// // そのほか _ => (), } }); }
Get-ChildItemを使って、カレントディレクトリ以下の指定した拡張子のファイルを一括削除できる。
これを利用して、VC++のプロジェクト内のいらないファイルを一括削除できる。
Get-ChildItem -Include *.aps,*.dep,*.idb,*.ilk,*.ncb,*.obj,*.pch,*.pdb,*.res,*.suo,*.user -Recurse | del
この時、引数に -WhatIf オプションを指定すると、実際にファイルが削除されずに、どのファイルが削除されるのかを見ることができる。
Get-ChildItem -Include *.aps,*.dep,*.idb,*.ilk,*.ncb,*.obj,*.pch,*.pdb,*.res,*.suo,*.user -Recurse | del -WhatIf