- 投稿日:2020-05-17T23:32:48+09:00
Arduinoの測定値をNode.jsで受けてSocket.ioとchart.jsでリアルタイムにグラフ表示
概要
Arduinoの測定値をリアルタイムにグラフ表示してみたかったので、Node.js諸々を用いてブラウザ上にグラフ表示してみました。
今回はひとまず光センサーを測定対象にしました。光センサーの抵抗値の変化を電圧としてArduinoのアナログ入力で測定しています。普段コーディングしない人間のコードなので変な箇所が多々あるかもしれません。その辺はご了承ください。
構成
ハードウェアとソフトウェアの構成を示します。Arduino周りの回路はDEVICE PLUSの記事を参考にしてください。
ハードウェア
- PC (Mac)
- Arduino UNO (PCとUSB接続)
- 光センサー回路
- ブレッドボード
- 光センサー
- 抵抗(1kΩくらい)
- ジャンパー線
ソフトウェア
- Node.jsのフレームワークであるExpress
- Arduinoとシリアル通信するためのserialportライブラリ
- リアルタイム通信するためのSocket.ioライブラリ
- グラフ表示するためのchart.jsライブラリ
ソースコード
ソースコードは以下の4つです。
- Arduino
- serialCom.ino
- サーバ側
- app.js
- クライアント(ブラウザ)側
- index.html
- index.js
Arduino
serialCom.inoint analogPin=A3; double aval=0; double val=0; void setup() { Serial.begin(9600); } void loop() { aval = analogRead(analogPin); val = 5 * aval / 1024; Serial.println(val); delay(100); }アナログ入力のA3ピンと5V出力を使用しています。(使用する端子は回路によって変わります。)
analogReadで得られる数値は10bitのA/Dコンバータの出力コードなので、電圧に変換しています。測定間隔は100msにしました。サーバ側
app.jsvar express = require('express'); var app = express(); var http = require('http').Server(app); var io = require('socket.io')(http); const SerialPort = require('serialport'); const Readline = require('@serialport/parser-readline'); const port = new SerialPort('/dev/cu.usbmodem141101', { baudRate: 9600 }); const parser = new Readline(); port.pipe(parser); app.use(express.static('public')); app.get('/', function(req, res){ res.sendFile(__dirname + '/index.html'); }); // ブラウザ側とのコネクション確立 io.on('connection', (socket) => { console.log('a user connected'); socket.on('disconnect', () => { console.log('user disconnected'); }); }); //サーバ起動 http.listen(3000, function(){ console.log('listening on *:3000'); }); //Arduinoからデータを受信したらクライアントへ送信 parser.on('data', (data) => { io.emit('graph update', (data)); });必要なモジュールをインポートしてそれぞれ設定します。
Arduinoからのデータをシリアルポートで待ち受けます。(ポート名は環境によって変わります。)
httpサーバのポート3000で待ち受けます。
socket.ioでクライアントのブラウザと接続します。
Ardionoからシリアルポート経由でデータを受信したらブラウザへデータを送信します。クライアント側
index.html<!doctype html> <html> <head> <title>グラフテスト</title> <script src='https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.0/Chart.min.js'></script> <script src='//code.jquery.com/jquery-3.2.1.min.js'></script> <script src='http://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.2/moment.min.js'></script> <script src='//cdn.socket.io/socket.io-1.4.5.js'></script> </head> <body> <canvas id="canvas" width="500" height="500"></canvas> <script src='index.js'></script> </body> </html>jquery, chart.js, moment.js, socket.ioを読み込んでいます。
(moment.jsをバンドルしたchart.jsもあるみたいですが、今回はそのまま。)
canvasタグ内にchart.jsでグラフが描画されます。
script部分は別ファイルにしています。index.jsconst socket = io.connect(); var ctx = document.getElementById('canvas').getContext('2d'); // グラフの作成 var myChart = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: 'data-label1', data: [], backgroundColor: 'rgba(0,0,225,1)', borderColor: 'rgba(0,0,225,1)', borderWidth: 1, lineTension: 0, fill: false }] }, options: { title: { display: true, text: 'CHART TITLE' }, scales: { xAxes: [{ ticks: { //autoSkip: true, maxTicksLimit: 10 } }], yAxes: [{ ticks: { // beginAtZero:true, // autoSkip: true, // maxTicksLimit: 10, min:0, max:5, stepSize:1 } }] }, // グラフサイズ固定 responsive: false, //maintainAspectRatio: false } }); $(() => { // サーバから値を受け取った時の処理 socket.on('graph update', (recievedData) => { // 現在時刻の取得 const time = moment(); const outputTime = time.format('HH:mm:ss'); // 追加するデータのラベルに時間を追加 myChart.data.labels.push(outputTime); // グラフにデータを追加 myChart.data.datasets[0].data.push(recievedData); // データ数が100以上なら一番古い要素を削除 if (myChart.data.datasets[0].data.length > 100) { myChart.data.labels.shift(); myChart.data.datasets[0].data.shift(); }; // グラフの表示を更新 myChart.update(); }) });myChartがグラフの設定です。
その後に続くのがサーバからデータを受け取った時の処理です。
moment.jsでラベルに現在時刻を追加しています。
無限にデータが増え続けるので、データ数100を上限にしています。結果
ArduinoをUSBで接続してから、ターミナルで
node app.js
と叩いてサーバを起動、ブラウザで127.0.0.1:3000
へアクセスするとこんな感じのグラフが表示されます。
光センサーの上で手で光を遮ったりしたグラフになります。光を遮ると光センサーの抵抗値が上がるので電圧も上がります。
横軸がなぜか均等にならないのですが、気が向いたら改善します。
- 投稿日:2020-05-17T23:15:07+09:00
Docker 環境で、Spring Boot アプリを、Visual Studio Code で開発する
まだまだ、仕事的にはJavaでの開発も考えないとな・・・ということで久しぶりに。
主力と思われるSpring Boot をちょっと試してみます。まずはDocker環境
こちらを参照
VSCodeとDockerでSpring Boot + PostgreSQL開発環境を作る$ docker-compose up -d $ docker-compose ps -a $ docker exec -it sb_db /bin/bash # psql -d dev -U dev dev=# select * from Names; dev=# quit # exit $ docker-compose stop $ docker-compose rm -f「JavaコンテナからDBコンテナにアクセス」
からは若干環境を整えながらやる必要がありました。まず、VSCodeで下記二つの拡張を追加
Remote - SSH
Remote - Containersそうすると、REMOTE EXPLORER で、Containersが選択できるようになる。
選択すると、Dockerコンテナが一等表示される(こんなに作ってたんだ・・・)。右クリックして、Attache to container で別ウインドウで開かれる。
そちらのウインドウで、SQLToolsをインストール。
こんなふうにDBに接続できるんですね。さらに、Remote Development拡張をインストール。
そして、書かれているがままに進めていくと、無事プロジェクト作成成功。
デバッグ画面がちょっと異なっていてどまどいましたが、そのまま起動でOK。
Hello world!
続いて、DB接続用のファイルを追加。
追加していくと、勝手に最低限必要な記述が既に入っている。
今どきだな〜しかし、ここで実行してみるとエラー発生。
まあ、ソース書いてる段階でエラーになってましたが。javax.persistence
が解決されていません。build.gradlecompile 'org.springframework.boot:spring-boot-starter-data-jpa' // ← 追加(Spring Data JPA)と言う形で、implementation を、compile に変更するとOK。
そういうものなのかな・・・(gradle初体験中)これにより、無事データも表示されました。
テンプレートは当然のこと、コントローラを修正して保存すると、自動でビルドが動く感じですね。
これで基本の枠組みができました。
なお、この後、docker-compose.yml をちょっと修正。
今作業しているフォルダにJavaのソースも置くようにしたいと思い、volumes: の記述を追加しました。docker-compose.ymlversion: '3.2' services: db: image: postgres:latest container_name: sb_db # "Spring Boot 用のDB" という意味の任意の名前 restart: always # ports: # - 5432:5432 # 今回はコンテナ同士でつなぐだけなのでコメントアウト environment: POSTGRES_USER: 'dev' # DBのユーザー名(=DB名) POSTGRES_PASSWORD: 'pass' # DBのパスワード volumes: - "./docker/db/initdb:/docker-entrypoint-initdb.d" java: image: openjdk:11 container_name: sb restart: always ports: - 8080:8080 # ホストからデバッグできるようにポートフォーワード tty: true depends_on: - db volumes: - "./docker/java/src:/usr/local/src"画面デザイン
これでいわゆる普通のサーバアプリを開発する環境ができたわけですが、フロント側をどうするか。
templatesのHTMLにVueを組み込むと言うことを考えましたが、Vueを主として考えると、サーバ側はAPIの提供だけになる。
もちろんそれでも良いでのですが、今回の目的はJavaを主として開発する体制向けの調査なので、あまりVueの深いところに入っていくとちょっとずれてきてしまうので、その構成はやめました。そう考えた時に、画面デザインは、Vuetifyとか、Bulmaとか、Element UIとかいいなと思っていたので、普通にSpring Boot で開発する場合のテンプレートのようなものも色々ないかなと思ったのですが、そ言う言う観点ではあまり見つからず。
やはり、主力は、Bootstrap か、ということで、Bootstrapテンプレートで探すと色々出てくる。
その中から組み込みやすそうな、Honoka にしてみました。
Honoka一番シンプル?な、ダウンロード方式で行きます。
ダウンロードしたZipを展開するとhonokaフォルダができるので、それをそのまま、
・・・src/main/resources/static/honoka
に置きます。テンプレートファイルに、cssとjsを組み込めば、反映されているようです。
index.html<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <title>Demoアプリケーション</title> <link rel="stylesheet" type="text/css" href="honoka/css/bootstrap.min.css"> <meta charset="utf-8" /> </head> <body> <h1>Hello world!</h1> <p>Nameリスト</p> <div class="bs-component"> <table class="table table-hover"> <thead><tr><th>key</th><th>name</th></tr></thead> <tbody> <tr th:each="data : ${names}"><td th:text="${data.key}"></td><td th:text="${data.name}"></td></tr> </tbody> </table> </div> <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script> <script src="./honoka/js/bootstrap.min.js"></script> <script type="text/javascript"> $('.bs-component [data-toggle="popover"]').popover(); $('.bs-component [data-toggle="tooltip"]').tooltip(); </script> </body> </html>これで進めようと思ったのですが・・・
Azureへのデプロイ(失敗)
ここまでのところで、Azureにデプロイしようと考えました。
仕事的にはAzureを選択する可能性が一番高そうなので。Azureの場合、App ServiceでさーばスペックをF1にすると、無料で使用可能です。
ところがこれがなかなか難しい。
基本的には、ほとんどが手順は、Maven前提に書かれています。
例えば、
https://www.eisbahn.jp/yoichiro/2019/01/azure-gradle-plugin.html
Azure App Service/Functions向けのGradleプラグイン
という情報もありますが、これもちょっと私では分かりにくかった。まだまだ、Mavenが主力かと思い、いったん、
Spring Initializr: Generate a Gradle Project
じゃなくて
Spring Initializr: Generate a Maven Project
で再作成しました。
その際、このあ後で必要そうな気がして、MyBatis Frameworkも構成に含めました。Mavenでのビルドは、こんな感じだそう。
./mvnw clean package
./mvnw spring-boot:run
これで確かに起動確認はできているが、DB接続はできていない模様(DBからのリストが表示されていない)。けど、とりあえず動いているし、ということで、デプロイしてみることに。
Azure CLI のインストール
https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli?view=azure-cli-latestDebian または Ubuntu での apt を使用したインストール
(apt での Azure CLI のインストール)
https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-apt?view=azure-cli-latest# curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash bash: sudo: command not foundまじか
# apt-get update # apt-get install -y sudoこれで、cliからazにログイン。
で、
./mvnw com.microsoft.azure:azure-webapp-maven-plugin:1.8.0:configとすると、いろいろオプションを聞かれて、いろいろ進んで、
[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 06:30 min [INFO] Finished at: 2020-05-14T23:59:23Z [INFO] ------------------------------------------------------------------------ # ./mvnw azure-webapp:deploy [INFO] Successfully deployed the artifact to https://demo-1589500547098.azurewebsites.netとはなる。
しかし、エラー画面。詳細を見ると、org.postgresql.util.PSQLException: The connection attempt failed.まあ、そうだよね、DB立ててないからね。
ちなみに、上記のようにやって、App Serviceを自動で作成するようにすると、自動で作られたのは、Premium V2。
危険!!
プラン変更
https://docs.microsoft.com/ja-jp/azure/app-service/app-service-plan-manage
けど失敗してしまったので、いったん削除(リソースグループまで削除しないとエラーになる)。で、この後、いったんDB接続しないように修正して試したりしていたのですが、なかなかうまくいかない。そして、そもそも、結局Azureだと、DBまで無料で試すことはできない・・・。
ということで断念することにしました。Heroku へのデプロイ
そういうわけで、Herokuへデプロイすることにしました。
https://himeji-cs.jp/blog2/blog/2019/08/hello-heroku-with-springboot.html
こちらの「ローカルでビルドした jar をデプロイする」を大いに参考にさせていただきました。heroku-cliを入れて、javaプラグインを入れて、system.properties を作成して、Procfile を作成、という流れになります。
heroku deploy:jar で最初動いてくれませんでしたが、git管理フォルダでないと動かないようです。
今回の環境の場合、この作業をしているフォルダの配下に、docker/java/src/demo という階層構造を作っており、アプリケーションとしてはdemoが起点となるため、ここがgit管理フォルダになっていないといけないということです。これに対して、現状は、作業フォルダがgit管理フォルダです。
こんなことやって良いか分からなかったですが、demoフォルダで改めてgit init しました。結果的にはこれでOKでした。DBに接続しない状態でのデプロイができたので、続いて、DB環境を作って、接続します。
こちらを参考にしました。
https://mhaya18.hatenablog.com/entry/2018/11/25/151403https://devcenter.heroku.com/articles/getting-started-with-gradle-on-heroku#use-a-database
ここはさほど悩まずにすんなりいきました。
ダッシュボードテンプレート
さて、上記でクライアント側Bootstrapテンプレートを導入しましたが、改めてやろうとしていることを考えるとダッシュボード機能も欲しいぞと。
これもいろいろ悩んだ挙句、Charismaにしました。
決め手は、導入の手順がファイルコピーだけで良いということ。
これで見た目が整うことは分かりました。ただ、複数ページでヘッダ等を共有する仕組みになっていない。
すなわち、一つ修正したら全部修正が必要。
むむ。ここで、Thymeleaf 登場。これも、名前は聞いていたものの、初使用。
Thymeleafでヘッダーフッターを共通化する方法こちらを見ながら、公式も見ることで、Thymeleafの基本はよく分かりました。
ただ、肝心の,
body部に関して、その中に、サイドメニューがあり、フッタがあり、そこまではよいとして、さらに、body内で様々なscriptタグを使っている場合の上手いやり方が見えませんでした。と、今度は、こんなページを発見。
https://macchinetta.github.io/server-guideline-thymeleaf/current/ja/ArchitectureInDetail/WebApplicationDetail/TemplateLayout.htmlこれはよくできていると思ったのですが、このページはなんなんだろうと調べてみると、Macchinettaという、NTTのフレームワーク。
ん?NTTグループといえば、TERASOLUNAでは?と思ったところ、こちらのMacchinettaがリリースされましたによると、テラそるなを包含したフレームワーク?
とりあえず、素性も悪くないということで、ありがたく参照させていただきます。で、ここでいったんherokuに最新をデプロイしておこうと思ったらトラブル。
heroku deploy をしようとすると、jarがないと言われる。
mvnw clean package をしようとすると、targetフォルダを削除できないと言われる。
targetフォルダを削除すると、すぐ勝手に作られる。
・・・悩みました。現在、VSCodeのRemote Containers拡張から画面を開いている状態ですが、これを閉じると、自動で作成されなくなる。(ここにたどり着くまでに、コンテナ再起動等いろいろ試しましたが・・・)
ではということで、Remote Containers拡張を使用せず、
docker exec -it sb /bin/bashでコンテナ内に入って、mvnw clean package を実行しました。
これでjarが作られてしまえば、もう一度Remote Containers拡張で接続しても大丈夫。
無事デプロイしました。データ取得
最後にデータ取得。いったんここで最後にします。
DB接続の仕組みは動いているものの、既存のGoogleスプレッドシートのデータを使用するため、外部API呼び出しを、クライアント側からやれば良いのではと思いつき。であれば、Vueもやっておきたい、ということで、こちらを参考にさせていただきます。
CDN版Vue.js + axios でさくっとAjaxしてみる配置等はわざと変更してみましたが、基本的にはそのまま動作します。
感動です。なお、Google スプレッドシート のデータ公開の方法は、先日書いたこちらを参照。
RPGツクール 外部API連携これの「コード.gs」の内容をそのまま貼り付けて、公開すれば、そのURLでデータを取得できます。
訳わからないでしょうが、いったん公開。
https://jq-spring-boot-first.herokuapp.com/
今後は、コンテンツを整えながら、データ検索等の仕組みを作っていきます。
- 投稿日:2020-05-17T22:20:16+09:00
Javaのfor文
- 投稿日:2020-05-17T18:15:16+09:00
CDKの記述子、クラス名、3D構造の記述子かどうかを一覧に出力する
はじめに
CDKの記述子について記述子名、クラス名、3D構造の記述子かどうかを一覧に出力してみた。
環境
- CDK 1.4系
3D構造の記述子かどうかの調査
jarファイルを展開すると、descriptors.xmlに記述子やFingerprintの情報が記載されており、そこに以下の定義があった。この"name"は記述子1つ1つを指定しているわけではなく、カテゴリ的な指定になっているのため、これが何に対応するのか思考錯誤が必要であった。
<Group name="3D"> <Descriptor name="Autocorrelation3D" value="true"/> <Descriptor name="CPSA" value="true"/> <Descriptor name="GravitationalIndex" value="true"/> <Descriptor name="LengthOverBreadth" value="true"/> <Descriptor name="MomentOfInertia" value="true"/> <Descriptor name="PetitjeanShapeIndex" value="true"/> <Descriptor name="RDF" value="true"/> <Descriptor name="WHIM" value="true"/> </Group>プログラム
試行錯誤した結果の最終プログラムを示す。動作にはもちろんJavaが必要だ。上の"name"を参考に、CDKのDescriptorクラスと見比べていったところ、3D構造が必要となる記述子は、以下プログラムのdescriptors_3dの配列に示しているDescriptorクラスから生成されたもの、という結論とした(間違っているかもしれないが、後で訂正すればいいかな)。なお1つ1つの記述子のカテゴリ的な情報として使えそうなので、クラス名も出力するようにした。
CDKSample.javaimport org.openscience.cdk.qsar.IDescriptor; import org.openscience.cdk.qsar.descriptors.molecular.*; public class CDKSample { public static void main(String[] argv) throws Exception { IDescriptor[] descriptors_3d = new IDescriptor[]{ new AutocorrelationDescriptorPolarizability(), new CPSADescriptor(), new GravitationalIndexDescriptor(), new LengthOverBreadthDescriptor(), new MomentOfInertiaDescriptor(), new PetitjeanShapeIndexDescriptor(), new WHIMDescriptor() }; IDescriptor[] descriptors = new IDescriptor[]{ new SmallRingDescriptor(), new FractionalPSADescriptor(), // new EccentricConnectivityIndexDescriptor(), new ALOGPDescriptor(), //new AminoAcidCountDescriptor(), new AcidicGroupCountDescriptor(), new APolDescriptor(), new AromaticAtomsCountDescriptor(), new AromaticBondsCountDescriptor(), new AtomCountDescriptor(), new AutocorrelationDescriptorCharge(), new AutocorrelationDescriptorMass(), new AutocorrelationDescriptorPolarizability(), new BasicGroupCountDescriptor(), new BCUTDescriptor(), new BondCountDescriptor(), new BPolDescriptor(), new CarbonTypesDescriptor(), new ChiChainDescriptor(), new ChiClusterDescriptor(), new ChiPathClusterDescriptor(), new ChiPathDescriptor(), new CPSADescriptor(), new EccentricConnectivityIndexDescriptor(), new FMFDescriptor(), new FragmentComplexityDescriptor(), new GravitationalIndexDescriptor(), new HBondAcceptorCountDescriptor(), new HBondDonorCountDescriptor(), new HybridizationRatioDescriptor(), //new IPMolecularLearningDescriptor(), new KappaShapeIndicesDescriptor(), new KierHallSmartsDescriptor(), new LargestChainDescriptor(), new LargestPiSystemDescriptor(), new LengthOverBreadthDescriptor(), new LongestAliphaticChainDescriptor(), new MannholdLogPDescriptor(), new MDEDescriptor(), new MomentOfInertiaDescriptor(), new PetitjeanNumberDescriptor(), new PetitjeanShapeIndexDescriptor(), new RotatableBondsCountDescriptor(), new RuleOfFiveDescriptor(), new TPSADescriptor(), new VABCDescriptor(), new VAdjMaDescriptor(), new WeightDescriptor(), new WeightedPathDescriptor(), new WHIMDescriptor(), new WienerNumbersDescriptor(), new XLogPDescriptor(), new ZagrebIndexDescriptor() }; int count = 1; for (int i = 0; i < descriptors.length; i++) { IDescriptor descriptor = descriptors[i]; for (String name : descriptor.getDescriptorNames()) { System.out.print((count++) + ":" + descriptors[i].getClass()+","+name); int d3 = 0; for(IDescriptor descriptor_3d : descriptors_3d){ if(descriptor.getClass().equals(descriptor_3d.getClass())){ d3 = 1; break; } } System.out.println("," + d3); } } } }結果
こんな感じ
class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nSmallRings,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nAromRings,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRingBlocks,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nAromBlocks,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRings3,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRings4,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRings5,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRings6,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRings7,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRings8,0 class org.openscience.cdk.qsar.descriptors.molecular.SmallRingDescriptor,nRings9,0 class org.openscience.cdk.qsar.descriptors.molecular.FractionalPSADescriptor,tpsaEfficiency,0 class org.openscience.cdk.qsar.descriptors.molecular.AcidicGroupCountDescriptor,nAcid,0 class org.openscience.cdk.qsar.descriptors.molecular.ALOGPDescriptor,ALogP,0 class org.openscience.cdk.qsar.descriptors.molecular.ALOGPDescriptor,ALogp2,0 class org.openscience.cdk.qsar.descriptors.molecular.ALOGPDescriptor,AMR,0 class org.openscience.cdk.qsar.descriptors.molecular.APolDescriptor,apol,0 class org.openscience.cdk.qsar.descriptors.molecular.AromaticAtomsCountDescriptor,naAromAtom,0 class org.openscience.cdk.qsar.descriptors.molecular.AromaticBondsCountDescriptor,nAromBond,0 class org.openscience.cdk.qsar.descriptors.molecular.AtomCountDescriptor,nAtom,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorCharge,ATSc1,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorCharge,ATSc2,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorCharge,ATSc3,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorCharge,ATSc4,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorCharge,ATSc5,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorMass,ATSm1,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorMass,ATSm2,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorMass,ATSm3,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorMass,ATSm4,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorMass,ATSm5,0 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorPolarizability,ATSp1,1 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorPolarizability,ATSp2,1 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorPolarizability,ATSp3,1 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorPolarizability,ATSp4,1 class org.openscience.cdk.qsar.descriptors.molecular.AutocorrelationDescriptorPolarizability,ATSp5,1 class org.openscience.cdk.qsar.descriptors.molecular.BasicGroupCountDescriptor,nBase,0 class org.openscience.cdk.qsar.descriptors.molecular.BCUTDescriptor,BCUTw-1l,0 class org.openscience.cdk.qsar.descriptors.molecular.BCUTDescriptor,BCUTw-1h,0 class org.openscience.cdk.qsar.descriptors.molecular.BCUTDescriptor,BCUTc-1l,0 class org.openscience.cdk.qsar.descriptors.molecular.BCUTDescriptor,BCUTc-1h,0 class org.openscience.cdk.qsar.descriptors.molecular.BCUTDescriptor,BCUTp-1l,0 class org.openscience.cdk.qsar.descriptors.molecular.BCUTDescriptor,BCUTp-1h,0 class org.openscience.cdk.qsar.descriptors.molecular.BondCountDescriptor,nB,0 class org.openscience.cdk.qsar.descriptors.molecular.BPolDescriptor,bpol,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C1SP1,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C2SP1,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C1SP2,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C2SP2,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C3SP2,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C1SP3,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C2SP3,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C3SP3,0 class org.openscience.cdk.qsar.descriptors.molecular.CarbonTypesDescriptor,C4SP3,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,SCH-3,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,SCH-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,SCH-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,SCH-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,SCH-7,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,VCH-3,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,VCH-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,VCH-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,VCH-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiChainDescriptor,VCH-7,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,SC-3,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,SC-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,SC-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,SC-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,VC-3,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,VC-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,VC-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiClusterDescriptor,VC-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathClusterDescriptor,SPC-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathClusterDescriptor,SPC-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathClusterDescriptor,SPC-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathClusterDescriptor,VPC-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathClusterDescriptor,VPC-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathClusterDescriptor,VPC-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-0,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-1,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-2,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-3,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,SP-7,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-0,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-1,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-2,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-3,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-4,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-5,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-6,0 class org.openscience.cdk.qsar.descriptors.molecular.ChiPathDescriptor,VP-7,0 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,PPSA-1,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,PPSA-2,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,PPSA-3,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,PNSA-1,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,PNSA-2,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,PNSA-3,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,DPSA-1,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,DPSA-2,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,DPSA-3,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,FPSA-1,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,FPSA-2,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,FPSA-3,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,FNSA-1,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,FNSA-2,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,FNSA-3,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,WPSA-1,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,WPSA-2,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,WPSA-3,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,WNSA-1,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,WNSA-2,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,WNSA-3,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,RPCG,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,RNCG,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,RPCS,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,RNCS,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,THSA,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,TPSA,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,RHSA,1 class org.openscience.cdk.qsar.descriptors.molecular.CPSADescriptor,RPSA,1 class org.openscience.cdk.qsar.descriptors.molecular.EccentricConnectivityIndexDescriptor,ECCEN,0 class org.openscience.cdk.qsar.descriptors.molecular.FMFDescriptor,FMF,0 class org.openscience.cdk.qsar.descriptors.molecular.FragmentComplexityDescriptor,fragC,0 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAV-1,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAV-2,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAV-3,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAVH-1,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAVH-2,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAVH-3,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAV-4,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAV-5,1 class org.openscience.cdk.qsar.descriptors.molecular.GravitationalIndexDescriptor,GRAV-6,1 class org.openscience.cdk.qsar.descriptors.molecular.HBondAcceptorCountDescriptor,nHBAcc,0 class org.openscience.cdk.qsar.descriptors.molecular.HBondDonorCountDescriptor,nHBDon,0 class org.openscience.cdk.qsar.descriptors.molecular.HybridizationRatioDescriptor,HybRatio,0 class org.openscience.cdk.qsar.descriptors.molecular.KappaShapeIndicesDescriptor,Kier1,0 class org.openscience.cdk.qsar.descriptors.molecular.KappaShapeIndicesDescriptor,Kier2,0 class org.openscience.cdk.qsar.descriptors.molecular.KappaShapeIndicesDescriptor,Kier3,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sLi,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssBe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssBe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssBH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssB,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssB,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sCH3,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dCH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssCH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.tCH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dsCH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aaCH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssCH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ddC,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.tsC,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dssC,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aasC,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aaaC,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssC,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sNH3,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sNH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssNH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dNH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssNH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aaNH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.tN,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssNH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dsN,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aaN,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssN,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ddsN,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aasN,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssN,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sOH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dO,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssO,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aaO,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sF,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sSiH3,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssSiH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssSiH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssSi,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sPH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssPH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssP,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dsssP,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssssP,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sSH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dS,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssS,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aaS,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dssS,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ddssS,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sCl,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sGeH3,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssGeH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssGeH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssGe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sAsH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssAsH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssAs,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssdAs,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssssAs,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sSeH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dSe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssSe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.aaSe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.dssSe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ddssSe,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sBr,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sSnH3,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssSnH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssSnH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssSn,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sI,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sPbH3,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssPbH2,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.sssPbH,0 class org.openscience.cdk.qsar.descriptors.molecular.KierHallSmartsDescriptor,khs.ssssPb,0 class org.openscience.cdk.qsar.descriptors.molecular.LargestChainDescriptor,nAtomLC,0 class org.openscience.cdk.qsar.descriptors.molecular.LargestPiSystemDescriptor,nAtomP,0 class org.openscience.cdk.qsar.descriptors.molecular.LengthOverBreadthDescriptor,LOBMAX,1 class org.openscience.cdk.qsar.descriptors.molecular.LengthOverBreadthDescriptor,LOBMIN,1 class org.openscience.cdk.qsar.descriptors.molecular.LongestAliphaticChainDescriptor,nAtomLAC,0 class org.openscience.cdk.qsar.descriptors.molecular.MannholdLogPDescriptor,MLogP,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-11,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-12,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-13,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-14,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-22,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-23,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-24,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-33,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-34,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEC-44,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEO-11,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEO-12,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEO-22,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEN-11,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEN-12,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEN-13,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEN-22,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEN-23,0 class org.openscience.cdk.qsar.descriptors.molecular.MDEDescriptor,MDEN-33,0 class org.openscience.cdk.qsar.descriptors.molecular.MomentOfInertiaDescriptor,MOMI-X,1 class org.openscience.cdk.qsar.descriptors.molecular.MomentOfInertiaDescriptor,MOMI-Y,1 class org.openscience.cdk.qsar.descriptors.molecular.MomentOfInertiaDescriptor,MOMI-Z,1 class org.openscience.cdk.qsar.descriptors.molecular.MomentOfInertiaDescriptor,MOMI-XY,1 class org.openscience.cdk.qsar.descriptors.molecular.MomentOfInertiaDescriptor,MOMI-XZ,1 class org.openscience.cdk.qsar.descriptors.molecular.MomentOfInertiaDescriptor,MOMI-YZ,1 class org.openscience.cdk.qsar.descriptors.molecular.MomentOfInertiaDescriptor,MOMI-R,1 class org.openscience.cdk.qsar.descriptors.molecular.PetitjeanNumberDescriptor,PetitjeanNumber,0 class org.openscience.cdk.qsar.descriptors.molecular.PetitjeanShapeIndexDescriptor,topoShape,1 class org.openscience.cdk.qsar.descriptors.molecular.PetitjeanShapeIndexDescriptor,geomShape,1 class org.openscience.cdk.qsar.descriptors.molecular.RotatableBondsCountDescriptor,nRotB,0 class org.openscience.cdk.qsar.descriptors.molecular.RuleOfFiveDescriptor,LipinskiFailures,0 class org.openscience.cdk.qsar.descriptors.molecular.TPSADescriptor,TopoPSA,0 class org.openscience.cdk.qsar.descriptors.molecular.VABCDescriptor,VABC,0 class org.openscience.cdk.qsar.descriptors.molecular.VAdjMaDescriptor,VAdjMat,0 class org.openscience.cdk.qsar.descriptors.molecular.WeightDescriptor,MW,0 class org.openscience.cdk.qsar.descriptors.molecular.WeightedPathDescriptor,WTPT-1,0 class org.openscience.cdk.qsar.descriptors.molecular.WeightedPathDescriptor,WTPT-2,0 class org.openscience.cdk.qsar.descriptors.molecular.WeightedPathDescriptor,WTPT-3,0 class org.openscience.cdk.qsar.descriptors.molecular.WeightedPathDescriptor,WTPT-4,0 class org.openscience.cdk.qsar.descriptors.molecular.WeightedPathDescriptor,WTPT-5,0 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wlambda1.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wlambda2.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wlambda3.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wnu1.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wnu2.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wgamma1.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wgamma2.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Wgamma3.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Weta1.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Weta2.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,Weta3.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,WT.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,WA.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,WV.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,WK.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,WG.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WHIMDescriptor,WD.unity,1 class org.openscience.cdk.qsar.descriptors.molecular.WienerNumbersDescriptor,WPATH,0 class org.openscience.cdk.qsar.descriptors.molecular.WienerNumbersDescriptor,WPOL,0 class org.openscience.cdk.qsar.descriptors.molecular.XLogPDescriptor,XLogP,0 class org.openscience.cdk.qsar.descriptors.molecular.ZagrebIndexDescriptor,Zagreb,0
- 投稿日:2020-05-17T16:36:04+09:00
Javaのwhile文
while文
while文は一定の処理を自動で繰り返し処理をしたいときに使います。
例えば、1~100までの数字をコンソールに出力したい場合、System.out.println()を100回書くのはコードが冗長になってしまいます。そこで、while文を使います。Main.javawhile (条件) { 繰り返す処理; }上記のwhile文を使えば繰り返し処理を実行できます。
【例】Main.javaint x = 3; while (x <= 3) [ System.out.println(x + "位"); x++; }上記はx = 3で変数xを定義しています。x++;はxに1を追加していきます。
変数に1を足していきますので、そうすると4回目の繰り返しでwhileの条件がfalseになり、繰り返し処理が終了します。【例】
Main.javaint x =5; while (x > 0) [ System.out.println(x); x--; }上記はが0より大きい場合に繰り返し処理をします。
- 投稿日:2020-05-17T16:36:04+09:00
Javaのwhile文とfor文
while文
while文は一定の処理を自動で繰り返し処理をしたいときに使います。
例えば、1~100までの数字をコンソールに出力したい場合、System.out.println()を100回書くのはコードが冗長になってしまいます。そこで、while文を使います。
繰り返し処理の流れとしては
①変数の初期化
②条件式
③繰り返す処理
④変数の更新
そこからまた
②条件式
③繰り返す処理
④変数の更新
と、繰り返します。Main.javawhile (条件) { 繰り返す処理; }上記のwhile文を使えば繰り返し処理を実行できます。
【例】Main.javaint x = 3; while (x <= 3) [ System.out.println(x + "位"); x++; }上記はx = 3で変数xを定義しています。x++;はxに1を追加していきます。
変数に1を足していきますので、そうすると4回目の繰り返しでwhileの条件がfalseになり、繰り返し処理が終了します。【例】
Main.javaint x =5; while (x > 0) [ System.out.println(x); x--; }上記はが0より大きい場合に繰り返し処理をします。
for文
while文と一緒でfor文も繰り返し処理の1つです。
繰り返し処理の流れとしてはwhile文と一緒で
①変数の初期化
②条件式
③繰り返す処理
④変数の更新
そこからまた
②条件式
③繰り返す処理
④変数の更新
と、繰り返します。
for文Main.javafor (変数の初期化;条件式;変数の更新) { System.out.println(繰り返す処理); }【例】
Main.javafor (int x = 1;x <= 10;x++) { System.out.println(x); }上記ように記述すると10回処理を繰り返します。
使うのであればfor文の方が使いやすいですね。continue
繰り返し処理を終了するにはbreakを使いますが、continueはその周の処理だけをスキップして、次の周を実行します。
【例】Main.javafor (int x = 1;x <= 10;x++) { if(x%5==0) { continue; } System.out.println(x); }上記のように記述すると、5の倍数の周のループを終了し、次のループを実行します。
- 投稿日:2020-05-17T16:21:29+09:00
JavaとJavaScriptでwebブラウザとのソケット通信
背景
ネット上で多人数同時通信のものを作成するため、ソケット通信について実践したことを記述。
チャットやオンラインゲームのような、複数でのリアルタイム通信を希望。「webブラウザ」で実行できるようなプログラムを「Java」で組みたかったのだが、ググってもあまり出てこないので記述しておく。
(知識不足でネットワークプログラミングという言葉も知らなかったため、ググり方に問題があるかも…)データの読み書き等の超基本的なところからスタート。
ソケット通信
通信方法の1種。
サーバーとクライアントに対して、HTTPのgetアクセスやpostアクセスのように単一方向への通信ではなく、リアルタイムでの双方向通信が可能。
⇒ サーバープログラムとクライアントプログラムの2つが必要。詳しくはググってほしい。
実行環境
- windowds
- eclipse (Tomcat8.0)
- Google Chrome
言語
サーバープログラム:Java
クライアントプログラム:JavaScript成果物
- webブラウザで動くチャットアプリ
開発手順
- クライアント側のプログラムを組む
- サーバー側のプログラムを組む
- クライアントプログラムからサーバーへアクセス
- サーバーからの応答があればOK
実践項目
- クライアントエコーによるソケット通信
- 単一クライアントとサーバーのチャットアプリ
- 複数クライアントとサーバーのチャットアプリ
- 複数クライアントでのじゃんけんシステム
1. クライアントエコーによるソケット通信
クライアント側のプログラムはJavaScriptで記述する。
ここはほぼパクり。
サーバー側のプログラムは必要なし。<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>WebSocket通信</title> <script type="text/javascript"> var wSck= new WebSocket("echo.websocket.org");// WebSocketオブジェクト生成 wSck.onopen = function() {//ソケット接続時のアクション document.getElementById('show').innerHTML += "接続しました。" + "<br/>"; }; wSck.onmessage = function(e) {//メッセージを受け取ったときのアクション document.getElementById('show').innerHTML += e.data + "<br/>"; }; var sendMsg = function(val) {//メッセージを送信するときのアクション var line = document.getElementById('msg');//入力内容を取得 wSck.send(line.value);//ソケットに送信 line.value = "";//内容をクリア }; </script> </head> <body> <div style="width: 500px; height: 200px; overflow-y: auto; border: 1px solid #333;" id="show"></div> <input type="text" size="80" id="msg" name="msg" /> <input type="button" value="送信" onclick="sendMsg();" /> </body> </html>コードの解説
1.Webソケットオブジェクトを生成し、引数にアドレスを代入。
var 変数 = new WebSocket("ws://IPアドレス:ポート番号");
今回はアドレスに「echo.websocket.org」を使用。
⇒クライアントから送信したものをそのまま返してくれる。
サーバープログラムを作成しなくてもソケット通信が可能なため便利。2.ソケット接続時のアクションを記述
ソケット.onopen = function() {・・・};
ソケット接続に成功したら、まずこの関数が実行される。3.メッセージを受け取ったときのアクションを記述
ソケット.onmessage = function() {・・・};
サーバーからデータが送られて来たらこの関数が実行される。4.メッセージを送る内容を記述
ソケット.send(・・・);
実行結果
送信した内容がそのまま架空サーバーから送り返されて表示される。
2. 単一クライアントとサーバーのチャットアプリ
架空サーバーじゃなく、実際にサーバーを作成してクライアントからの送信内容をそのまま返信する。
サーバー側のプログラムをJavaで記述する。import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Base64.Encoder; class SocketFree { private static ServerSocket sSck;// サーバー用のソケット private static Socket sck;// 受付用のソケット private static InputStream is;// 入力ストリーム private static InputStreamReader isr;// 入力ストリームを読み込む private static BufferedReader in;// バッファリングによるテキスト読み込み private static OutputStream os;// 出力ストリーム public static void main(String[] args) { try { sSck=new ServerSocket(60000);//サーバーソケットのインスタンスを作成(ポートは60000) System.out.println("サーバーに接続したよ!"); sck=sSck.accept();//接続待ち。来たらソケットに代入。 System.out.println( "参加者が接続しました!"); // 必要な入出力ストリームを作成する is=sck.getInputStream();//ソケットからの入力をバイト列として読み取る isr=new InputStreamReader(is);//読み取ったバイト列を変換して文字列を読み込む in=new BufferedReader(isr);//文字列をバッファリングして(まとめて)読み込む os=sck.getOutputStream();//ソケットにバイト列を書き込む //クライアントへ接続許可を返す(ハンドシェイク) handShake(in , os); //ソケットへの入力をそのまま返す ※125文字まで(126文字以上はヘッダーのフレームが変化する) echo(is , os); } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } //クライアントへ接続許可を返すメソッド(ハンドシェイク) public static void handShake(BufferedReader in , OutputStream os){ String header = "";//ヘッダーの変数宣言 String key = "";//ウェブソケットキーの変数宣言 try { while (!(header = in.readLine()).equals("")) {//入力ストリームから得たヘッダーを文字列に代入し、全行ループ。 System.out.println(header);//1行ごとにコンソールにヘッダーの内容を表示 String[] spLine = header.split(":");//1行を「:」で分割して配列に入れ込む if (spLine[0].equals("Sec-WebSocket-Key")) {//Sec-WebSocket-Keyの行 key = spLine[1].trim();//空白をトリムし、ウェブソケットキーを入手 } } key +="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";//キーに謎の文字列を追加する byte[] keyUtf8=key.getBytes("UTF-8");//キーを「UTF-8」のバイト配列に変換する MessageDigest md = MessageDigest.getInstance("SHA-1");//指定されたダイジェスト・アルゴリズムを実装するオブジェクトを返す byte[] keySha1=md.digest(keyUtf8);//キー(UTF-8)を使用してダイジェスト計算を行う Encoder encoder = Base64.getEncoder();//Base64のエンコーダーを用意 byte[] keyBase64 = encoder.encode(keySha1);//キー(SHA-1)をBase64でエンコード String keyNext = new String(keyBase64);//キー(Base64)をStringへ変換 byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Accept: " + keyNext + "\r\n\r\n") .getBytes("UTF-8");//HTTP レスポンスを作成 os.write(response);//HTTP レスポンスを送信 } catch (IOException e) { System.err.println("エラーが発生しました: " + e); } catch (NoSuchAlgorithmException e) { System.err.println("エラーが発生しました: " + e); } } //ソケットへの入力を無限ループで監視するメソッド public static void echo(InputStream is , OutputStream os) { try{ while(true) { byte[] buff = new byte[1024];//クライアントから送られたバイナリデータを入れる配列 int lineData =is.read(buff);//データを読み込む for (int i = 0; i < lineData - 6; i++) { buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);//7バイト目以降を3-6バイト目のキーを用いてデコード } String line = new String(buff, 6, lineData - 6, "UTF-8");//デコードしたデータを文字列に変換 byte[] sendHead = new byte[2];//送り返すヘッダーを用意 sendHead[0] = buff[0];//1バイト目は同じもの sendHead[1] = (byte) line.getBytes("UTF-8").length;//2バイト目は文字列の長さ os.write(sendHead);//ヘッダー出力 os.write(line.getBytes("UTF-8"));//3バイト目以降に文字列をバイナリデータに変換して出力 if (line.equals("bye")) break;//「bye」が送られたなら受信終了 } } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } }コードの解説
1.ソケットやストリーム等の必要な変数を宣言。
2.サーバーソケットのインスタンスを作成。
ServerSocket 変数 = new ServerSocket(ポート番号)
3.acceptメソッドでクライアントからの接続を待機。
4.接続されたクライアントの入出力ストリームを作成する。
5.クライアントからの接続要求に対してレスポンスを返す。(handShakeメソッド)
リクエストのヘッダーに必要な情報が記述されているため、編集して送り返す。
編集内容の詳細はコードのコメント及び参照サイトを参考。
要は、リクエストのヘッダーにある『WebSocketキー』を送り返す必要がある。
⇒ 『ハンドシェイク』6.ソケット接続後、無限ループでクライアントからの入力をそのまま返す。(echoメソッド)
クライアントから送信されたデータはバイナリデータとしてエンコードされている。
サーバー側でデコードし、再度エンコードしてクライアントへ送信する。
デコード内容の詳細はコードのコメント及び参照サイトを参考。
※送信された文字列の長さによってヘッダーのフレームが変化するので、ここでは簡単のため125文字以下に限定する。7.クライアントから「bye」が送信されるとループを抜け受信終了
実行結果
- サーバープログラムを起動
- ポート番号を指定し、localhostへ接続
※クライアントプログラムのアドレスを変更しておく(今回は60000番)
var 変数 = new WebSocket("ws://127.0.0.1:60000");
架空サーバー(echo.websocket.org)を使用したときと同様の結果が得られた。
今回はエコーするだけだが、サーバー側でデータをいろいろ弄って返信することも可能。複数クライアントとサーバーのチャットアプリ
サーバープログラムを編集し、複数のクライアントからの接続を許可する。
また、各クライアント間でリアルタイム通信となるようにする。import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Base64.Encoder; class SocketFree { private static ServerSocket sSck;// サーバー用のソケット private static Socket[] sck;// 受付用のソケット private static InputStream[] is;// 入力ストリーム private static InputStreamReader[] isr;// 入力ストリームを読み込む private static BufferedReader[] in;// バッファリングによるテキスト読み込み private static OutputStream[] os;// 出力ストリーム private static ClientThread user[];//各クライアントのインスタンス private static int member;// 接続しているメンバーの数 public static void main(String[] args) { int n=0; int maxUser=100; //各フィールドの配列を用意 sck=new Socket[maxUser]; is=new InputStream[maxUser]; isr=new InputStreamReader[maxUser]; in=new BufferedReader[maxUser]; os=new OutputStream[maxUser]; user=new ClientThread[maxUser]; try { sSck=new ServerSocket(60000);//サーバーソケットのインスタンスを作成(ポートは60000) System.out.println("サーバーに接続したよ!"); while(true) { sck[n]=sSck.accept();//接続待ち。来たらソケットに代入。 System.out.println( (n+1)+"番目の参加者が接続しました!"); // 必要な入出力ストリームを作成する is[n]=sck[n].getInputStream();//ソケットからの入力をバイト列として読み取る isr[n]=new InputStreamReader(is[n]);//読み取ったバイト列を変換して文字列を読み込む in[n]=new BufferedReader(isr[n]);//文字列をバッファリングして(まとめて)読み込む os[n]=sck[n].getOutputStream();//ソケットにバイト列を書き込む //クライアントへ接続許可を返す(ハンドシェイク) handShake(in[n] , os[n]); //各クライアントのスレッドを作成 user[n] = new ClientThread(n , sck[n] , is[n] , isr[n] , in[n] , os[n]); user[n].start(); member=n+1;//接続数の更新 n++;//次の接続者へ } } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } //クライアントへ接続許可を返すメソッド(ハンドシェイク) public static void handShake(BufferedReader in , OutputStream os){ String header = "";//ヘッダーの変数宣言 String key = "";//ウェブソケットキーの変数宣言 try { while (!(header = in.readLine()).equals("")) {//入力ストリームから得たヘッダーを文字列に代入し、全行ループ。 System.out.println(header);//1行ごとにコンソールにヘッダーの内容を表示 String[] spLine = header.split(":");//1行を「:」で分割して配列に入れ込む if (spLine[0].equals("Sec-WebSocket-Key")) {//Sec-WebSocket-Keyの行 key = spLine[1].trim();//空白をトリムし、ウェブソケットキーを入手 } } key +="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";//キーに謎の文字列を追加する byte[] keyUtf8=key.getBytes("UTF-8");//キーを「UTF-8」のバイト配列に変換する MessageDigest md = MessageDigest.getInstance("SHA-1");//指定されたダイジェスト・アルゴリズムを実装するオブジェクトを返す byte[] keySha1=md.digest(keyUtf8);//キー(UTF-8)を使用してダイジェスト計算を行う Encoder encoder = Base64.getEncoder();//Base64のエンコーダーを用意 byte[] keyBase64 = encoder.encode(keySha1);//キー(SHA-1)をBase64でエンコード String keyNext = new String(keyBase64);//キー(Base64)をStringへ変換 byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Accept: " + keyNext + "\r\n\r\n") .getBytes("UTF-8");//HTTP レスポンスを作成 os.write(response);//HTTP レスポンスを送信 } catch (IOException e) { System.err.println("エラーが発生しました: " + e); } catch (NoSuchAlgorithmException e) { System.err.println("エラーが発生しました: " + e); } } //各クライアントからのデータを全クライアントに送信するメソッド public static void sendAll(int number , byte[] sendHead , String line){ try { for (int i = 0; i <member ; i++) { os[i].write(sendHead);//ヘッダー出力 os[i].write(line.getBytes("UTF-8"));//3バイト目以降に文字列をバイナリデータに変換して出力 System.out.println((i+1)+"番目に"+(number+1)+"番目のメッセージを送りました!" ); } } catch (IOException e) { System.err.println("エラーが発生しました: " + e); } } } class ClientThread extends Thread{ //各クライアントのフィールド private int myNumber; private Socket mySck; private InputStream myIs; private InputStreamReader myIsr; private BufferedReader myIn; private OutputStream myOs; //コンストラクタでインスタンスのフィールドに各値を代入 public ClientThread(int n , Socket sck , InputStream is , InputStreamReader isr , BufferedReader in , OutputStream os) { myNumber=n; mySck=sck; myIs=is; myIsr=isr; myIn=in; myOs=os; } //Threadクラスのメイン public void run() { try { echo(myIs , myOs , myNumber); } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } //ソケットへの入力を無限ループで監視する ※125文字まで(126文字以上はヘッダーのフレームが変化する) public void echo(InputStream is , OutputStream os , int myNumber) { try{ while(true) { byte[] buff = new byte[1024];//クライアントから送られたバイナリデータを入れる配列 int lineData =is.read(buff);//データを読み込む for (int i = 0; i < lineData - 6; i++) { buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);//7バイト目以降を3-6バイト目のキーを用いてデコード } String line = new String(buff, 6, lineData - 6, "UTF-8");//デコードしたデータを文字列に変換 byte[] sendHead = new byte[2];//送り返すヘッダーを用意 sendHead[0] = buff[0];//1バイト目は同じもの sendHead[1] = (byte) line.getBytes("UTF-8").length;//2バイト目は文字列の長さ SocketFree.sendAll(myNumber , sendHead , line);//各クライアントへの送信は元クラスのsedAllメソッドで実行 if (line.equals("bye")) break;//「bye」が送られたなら受信終了 } } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } }コードの解説
1.クライアントのナンバリングや接続数を把握するためのフィールド変数を追加。
2.複数のクライアントに対応するため、各フィールドを配列形式にする。
main関数で配列の初期化を行い、これまでの変数を編集。3.複数クライアントを同時並行で処理するため、Threadクラスを継承したClientThreadクラスを作成する。
ハンドシェイクまではmain関数内で処理。
それ以降は各クライアント毎にClientThreadインスタンスを作成して並行処理。Threadクラス:main関数から分岐して同時に処理することができる。
.start();でrun()が実行される謎仕様。4.ClientThreadクラスにecho関数を移動。
データ受信 ⇒ デコード ⇒ データ送信
この内データ送信はmain関数が所属するクラスで実行するため、新しくsendAllメソッドを作成。
※データ送信は全クライアントに対して実行。
全クライアントを扱っているのはmain関数のクラス。5.その他、メソッドの型や変数名等の細かい修正。
実行結果
- サーバープログラムを起動
- 複数のブラウザからlocalhostへ接続
1つのクライアントから送信すると、全てのブラウザで同じテキストが表示される。
どのクライアントからテキストを送信しても表示結果は同じ。その他
改善点
・ブラウザを閉じたときのエラー処理
「bye」を送信すると監視のループを抜けてエラーなく終了できるが、監視中にブラウザを閉じるとエラーが発生しサーバーが停止してしまう。・closeメソッドの追記
本来はソケット通信を終了する際はcloseメソッドを使用して切断するが、今回は使用していない。・デコード処理の強化
デコードの処理を甘えた為、125文字以下の通信しかできない。
126文字以上になるとwebソケットのフレームが変化するため、場合分けが必要になってくる。・セキュリティ関連の強化
今回は何も対策しておらずセキュリティがガバガバなので、送受信の方法を考える必要がある。備考
・ポートの解放
プログラム内で指定したポートが既に使用されている場合、サーバー起動時にエラーが発生する。
1. コマンドプロンプトで>netstat -ano
を実行
2. 指定したローカルアドレスからPIDを確認
3. タスクマネージャーのプロセスからPDIのアプリケーションを確認
4. 必要ない物ならプロセスを終了してポートを開放
・ハンドシェイク
ソケット通信を行う前に、クライアントとサーバー間で合意を取る必要がある。
クライアントからサーバーへ接続要求があるなら、それに対してレスポンスを返す必要がある。
レスポンスの内容は参考ページに詳しく記述されている。・受信データのデコード
webソケットのデータにはフレームフォーマットが存在し、そのフレームに沿ってバイナリデータが送信されている。
データをデコードするには、フレーム構造を理解して1バイト毎に変換していく必要がある。
フレームフォーマットの詳細やデコード方法は参考ページに詳しく記述されている。感想
思った以上にややこしい。
ググってもなかなか出てこないし、マイナーな方法?
よく見かけるのは、サーバープログラムを「Node.js」で記述したもの。
ドットインストールの動画ではNode.jsで作成しており、レスポンスやデコード等の複雑な処理はしていなかった。
多分、Node.jsの勉強をした方が早い。Javaでも、アノテーションを利用するとデコード処理が無くても通信できるような記事も見かけたが、今回はパスした。
パスしない方が良かったかも。プログラミングの勉強をしていたはずが、いつの間にかプロトコルの勉強になっていた・・・
初学者が首を突っ込むところじゃない気がしたが、勉強になったので良し。間違えているところがあったらスマン。
気が付けば修正する。
改善すべき点もかなり多いので、可能なら更新していく。参考ページ
- 投稿日:2020-05-17T16:21:29+09:00
JavaとJavaScriptでwebブラウザのソケット通信
背景
ネット上で多人数同時通信のものを作成するため、ソケット通信について実践したことを記述。
チャットやオンラインゲームのような、複数でのリアルタイム通信を希望。「webブラウザ」で実行できるようなプログラムを「Java」で組みたかったのだが、ググってもあまり出てこないので記述しておく。
(知識不足でネットワークプログラミングという言葉も知らなかったため、ググり方に問題があるかも…)データの読み書き等の超基本的なところからスタート。
ソケット通信
通信方法の1種。
サーバーとクライアントに対して、HTTPのgetアクセスやpostアクセスのように単一方向への通信ではなく、リアルタイムでの双方向通信が可能。
⇒ サーバープログラムとクライアントプログラムの2つが必要。詳しくはググってほしい。
実行環境
- windowds
- eclipse (Tomcat8.0)
- Google Chrome
言語
サーバープログラム:Java
クライアントプログラム:JavaScript成果物
- webブラウザで動くチャットアプリ
開発手順
- クライアント側のプログラムを組む
- サーバー側のプログラムを組む
- クライアントプログラムからサーバーへアクセス
- サーバーからの応答があればOK
実践項目
- クライアントエコーによるソケット通信
- 単一クライアントとサーバーのチャットアプリ
- 複数クライアントとサーバーのチャットアプリ
- 複数クライアントでのじゃんけんシステム
1. クライアントエコーによるソケット通信
クライアント側のプログラムはJavaScriptで記述する。
ここはほぼパクり。
サーバー側のプログラムは必要なし。<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>WebSocket通信</title> <script type="text/javascript"> var wSck= new WebSocket("echo.websocket.org");// WebSocketオブジェクト生成 wSck.onopen = function() {//ソケット接続時のアクション document.getElementById('show').innerHTML += "接続しました。" + "<br/>"; }; wSck.onmessage = function(e) {//メッセージを受け取ったときのアクション document.getElementById('show').innerHTML += e.data + "<br/>"; }; var sendMsg = function(val) {//メッセージを送信するときのアクション var line = document.getElementById('msg');//入力内容を取得 wSck.send(line.value);//ソケットに送信 line.value = "";//内容をクリア }; </script> </head> <body> <div style="width: 500px; height: 200px; overflow-y: auto; border: 1px solid #333;" id="show"></div> <input type="text" size="80" id="msg" name="msg" /> <input type="button" value="送信" onclick="sendMsg();" /> </body> </html>コードの解説
1.Webソケットオブジェクトを生成し、引数にアドレスを代入。
var 変数 = new WebSocket("ws://IPアドレス:ポート番号");
今回はアドレスに「echo.websocket.org」を使用。
⇒クライアントから送信したものをそのまま返してくれる。
サーバープログラムを作成しなくてもソケット通信が可能なため便利。2.ソケット接続時のアクションを記述
ソケット.onopen = function() {・・・};
ソケット接続に成功したら、まずこの関数が実行される。3.メッセージを受け取ったときのアクションを記述
ソケット.onmessage = function() {・・・};
サーバーからデータが送られて来たらこの関数が実行される。4.メッセージを送る内容を記述
ソケット.send(・・・);
実行結果
送信した内容がそのまま架空サーバーから送り返されて表示される。
2. 単一クライアントとサーバーのチャットアプリ
架空サーバーじゃなく、実際にサーバーを作成してクライアントからの送信内容をそのまま返信する。
サーバー側のプログラムをJavaで記述する。import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Base64.Encoder; class SocketFree { private static ServerSocket sSck;// サーバー用のソケット private static Socket sck;// 受付用のソケット private static InputStream is;// 入力ストリーム private static InputStreamReader isr;// 入力ストリームを読み込む private static BufferedReader in;// バッファリングによるテキスト読み込み private static OutputStream os;// 出力ストリーム public static void main(String[] args) { try { sSck=new ServerSocket(60000);//サーバーソケットのインスタンスを作成(ポートは60000) System.out.println("サーバーに接続したよ!"); sck=sSck.accept();//接続待ち。来たらソケットに代入。 System.out.println( "参加者が接続しました!"); // 必要な入出力ストリームを作成する is=sck.getInputStream();//ソケットからの入力をバイト列として読み取る isr=new InputStreamReader(is);//読み取ったバイト列を変換して文字列を読み込む in=new BufferedReader(isr);//文字列をバッファリングして(まとめて)読み込む os=sck.getOutputStream();//ソケットにバイト列を書き込む //クライアントへ接続許可を返す(ハンドシェイク) handShake(in , os); //ソケットへの入力をそのまま返す ※125文字まで(126文字以上はヘッダーのフレームが変化する) echo(is , os); } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } //クライアントへ接続許可を返すメソッド(ハンドシェイク) public static void handShake(BufferedReader in , OutputStream os){ String header = "";//ヘッダーの変数宣言 String key = "";//ウェブソケットキーの変数宣言 try { while (!(header = in.readLine()).equals("")) {//入力ストリームから得たヘッダーを文字列に代入し、全行ループ。 System.out.println(header);//1行ごとにコンソールにヘッダーの内容を表示 String[] spLine = header.split(":");//1行を「:」で分割して配列に入れ込む if (spLine[0].equals("Sec-WebSocket-Key")) {//Sec-WebSocket-Keyの行 key = spLine[1].trim();//空白をトリムし、ウェブソケットキーを入手 } } key +="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";//キーに謎の文字列を追加する byte[] keyUtf8=key.getBytes("UTF-8");//キーを「UTF-8」のバイト配列に変換する MessageDigest md = MessageDigest.getInstance("SHA-1");//指定されたダイジェスト・アルゴリズムを実装するオブジェクトを返す byte[] keySha1=md.digest(keyUtf8);//キー(UTF-8)を使用してダイジェスト計算を行う Encoder encoder = Base64.getEncoder();//Base64のエンコーダーを用意 byte[] keyBase64 = encoder.encode(keySha1);//キー(SHA-1)をBase64でエンコード String keyNext = new String(keyBase64);//キー(Base64)をStringへ変換 byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Accept: " + keyNext + "\r\n\r\n") .getBytes("UTF-8");//HTTP レスポンスを作成 os.write(response);//HTTP レスポンスを送信 } catch (IOException e) { System.err.println("エラーが発生しました: " + e); } catch (NoSuchAlgorithmException e) { System.err.println("エラーが発生しました: " + e); } } //ソケットへの入力を無限ループで監視するメソッド public static void echo(InputStream is , OutputStream os) { try{ while(true) { byte[] buff = new byte[1024];//クライアントから送られたバイナリデータを入れる配列 int lineData =is.read(buff);//データを読み込む for (int i = 0; i < lineData - 6; i++) { buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);//7バイト目以降を3-6バイト目のキーを用いてデコード } String line = new String(buff, 6, lineData - 6, "UTF-8");//デコードしたデータを文字列に変換 byte[] sendHead = new byte[2];//送り返すヘッダーを用意 sendHead[0] = buff[0];//1バイト目は同じもの sendHead[1] = (byte) line.getBytes("UTF-8").length;//2バイト目は文字列の長さ os.write(sendHead);//ヘッダー出力 os.write(line.getBytes("UTF-8"));//3バイト目以降に文字列をバイナリデータに変換して出力 if (line.equals("bye")) break;//「bye」が送られたなら受信終了 } } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } }コードの解説
1.ソケットやストリーム等の必要な変数を宣言。
2.サーバーソケットのインスタンスを作成。
ServerSocket 変数 = new ServerSocket(ポート番号)
3.acceptメソッドでクライアントからの接続を待機。
4.接続されたクライアントの入出力ストリームを作成する。
5.クライアントからの接続要求に対してレスポンスを返す。(handShakeメソッド)
リクエストのヘッダーに必要な情報が記述されているため、編集して送り返す。
編集内容の詳細はコードのコメント及び参照サイトを参考。
要は、リクエストのヘッダーにある『WebSocketキー』を送り返す必要がある。
⇒ 『ハンドシェイク』6.ソケット接続後、無限ループでクライアントからの入力をそのまま返す。(echoメソッド)
クライアントから送信されたデータはバイナリデータとしてエンコードされている。
サーバー側でデコードし、再度エンコードしてクライアントへ送信する。
デコード内容の詳細はコードのコメント及び参照サイトを参考。
※送信された文字列の長さによってヘッダーのフレームが変化するので、ここでは簡単のため125文字以下に限定する。7.クライアントから「bye」が送信されるとループを抜け受信終了
実行結果
- サーバープログラムを起動
- ポート番号を指定し、localhostへ接続
※クライアントプログラムのアドレスを変更しておく(今回は60000番)
var 変数 = new WebSocket("ws://127.0.0.1:60000");
架空サーバー(echo.websocket.org)を使用したときと同様の結果が得られた。
今回はエコーするだけだが、サーバー側でデータをいろいろ弄って返信することも可能。複数クライアントとサーバーのチャットアプリ
サーバープログラムを編集し、複数のクライアントからの接続を許可する。
また、各クライアント間でリアルタイム通信となるようにする。import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Base64; import java.util.Base64.Encoder; class SocketFree { private static ServerSocket sSck;// サーバー用のソケット private static Socket[] sck;// 受付用のソケット private static InputStream[] is;// 入力ストリーム private static InputStreamReader[] isr;// 入力ストリームを読み込む private static BufferedReader[] in;// バッファリングによるテキスト読み込み private static OutputStream[] os;// 出力ストリーム private static ClientThread user[];//各クライアントのインスタンス private static int member;// 接続しているメンバーの数 public static void main(String[] args) { int n=0; int maxUser=100; //各フィールドの配列を用意 sck=new Socket[maxUser]; is=new InputStream[maxUser]; isr=new InputStreamReader[maxUser]; in=new BufferedReader[maxUser]; os=new OutputStream[maxUser]; user=new ClientThread[maxUser]; try { sSck=new ServerSocket(60000);//サーバーソケットのインスタンスを作成(ポートは60000) System.out.println("サーバーに接続したよ!"); while(true) { sck[n]=sSck.accept();//接続待ち。来たらソケットに代入。 System.out.println( (n+1)+"番目の参加者が接続しました!"); // 必要な入出力ストリームを作成する is[n]=sck[n].getInputStream();//ソケットからの入力をバイト列として読み取る isr[n]=new InputStreamReader(is[n]);//読み取ったバイト列を変換して文字列を読み込む in[n]=new BufferedReader(isr[n]);//文字列をバッファリングして(まとめて)読み込む os[n]=sck[n].getOutputStream();//ソケットにバイト列を書き込む //クライアントへ接続許可を返す(ハンドシェイク) handShake(in[n] , os[n]); //各クライアントのスレッドを作成 user[n] = new ClientThread(n , sck[n] , is[n] , isr[n] , in[n] , os[n]); user[n].start(); member=n+1;//接続数の更新 n++;//次の接続者へ } } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } //クライアントへ接続許可を返すメソッド(ハンドシェイク) public static void handShake(BufferedReader in , OutputStream os){ String header = "";//ヘッダーの変数宣言 String key = "";//ウェブソケットキーの変数宣言 try { while (!(header = in.readLine()).equals("")) {//入力ストリームから得たヘッダーを文字列に代入し、全行ループ。 System.out.println(header);//1行ごとにコンソールにヘッダーの内容を表示 String[] spLine = header.split(":");//1行を「:」で分割して配列に入れ込む if (spLine[0].equals("Sec-WebSocket-Key")) {//Sec-WebSocket-Keyの行 key = spLine[1].trim();//空白をトリムし、ウェブソケットキーを入手 } } key +="258EAFA5-E914-47DA-95CA-C5AB0DC85B11";//キーに謎の文字列を追加する byte[] keyUtf8=key.getBytes("UTF-8");//キーを「UTF-8」のバイト配列に変換する MessageDigest md = MessageDigest.getInstance("SHA-1");//指定されたダイジェスト・アルゴリズムを実装するオブジェクトを返す byte[] keySha1=md.digest(keyUtf8);//キー(UTF-8)を使用してダイジェスト計算を行う Encoder encoder = Base64.getEncoder();//Base64のエンコーダーを用意 byte[] keyBase64 = encoder.encode(keySha1);//キー(SHA-1)をBase64でエンコード String keyNext = new String(keyBase64);//キー(Base64)をStringへ変換 byte[] response = ("HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Accept: " + keyNext + "\r\n\r\n") .getBytes("UTF-8");//HTTP レスポンスを作成 os.write(response);//HTTP レスポンスを送信 } catch (IOException e) { System.err.println("エラーが発生しました: " + e); } catch (NoSuchAlgorithmException e) { System.err.println("エラーが発生しました: " + e); } } //各クライアントからのデータを全クライアントに送信するメソッド public static void sendAll(int number , byte[] sendHead , String line){ try { for (int i = 0; i <member ; i++) { os[i].write(sendHead);//ヘッダー出力 os[i].write(line.getBytes("UTF-8"));//3バイト目以降に文字列をバイナリデータに変換して出力 System.out.println((i+1)+"番目に"+(number+1)+"番目のメッセージを送りました!" ); } } catch (IOException e) { System.err.println("エラーが発生しました: " + e); } } } class ClientThread extends Thread{ //各クライアントのフィールド private int myNumber; private Socket mySck; private InputStream myIs; private InputStreamReader myIsr; private BufferedReader myIn; private OutputStream myOs; //コンストラクタでインスタンスのフィールドに各値を代入 public ClientThread(int n , Socket sck , InputStream is , InputStreamReader isr , BufferedReader in , OutputStream os) { myNumber=n; mySck=sck; myIs=is; myIsr=isr; myIn=in; myOs=os; } //Threadクラスのメイン public void run() { try { echo(myIs , myOs , myNumber); } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } //ソケットへの入力を無限ループで監視する ※125文字まで(126文字以上はヘッダーのフレームが変化する) public void echo(InputStream is , OutputStream os , int myNumber) { try{ while(true) { byte[] buff = new byte[1024];//クライアントから送られたバイナリデータを入れる配列 int lineData =is.read(buff);//データを読み込む for (int i = 0; i < lineData - 6; i++) { buff[i + 6] = (byte) (buff[i % 4 + 2] ^ buff[i + 6]);//7バイト目以降を3-6バイト目のキーを用いてデコード } String line = new String(buff, 6, lineData - 6, "UTF-8");//デコードしたデータを文字列に変換 byte[] sendHead = new byte[2];//送り返すヘッダーを用意 sendHead[0] = buff[0];//1バイト目は同じもの sendHead[1] = (byte) line.getBytes("UTF-8").length;//2バイト目は文字列の長さ SocketFree.sendAll(myNumber , sendHead , line);//各クライアントへの送信は元クラスのsedAllメソッドで実行 if (line.equals("bye")) break;//「bye」が送られたなら受信終了 } } catch (Exception e) { System.err.println("エラーが発生しました: " + e); } } }コードの解説
1.クライアントのナンバリングや接続数を把握するためのフィールド変数を追加。
2.複数のクライアントに対応するため、各フィールドを配列形式にする。
main関数で配列の初期化を行い、これまでの変数を編集。3.複数クライアントを同時並行で処理するため、Threadクラスを継承したClientThreadクラスを作成する。
ハンドシェイクまではmain関数内で処理。
それ以降は各クライアント毎にClientThreadインスタンスを作成して並行処理。Threadクラス:main関数から分岐して同時に処理することができる。
.start();でrun()が実行される謎仕様。4.ClientThreadクラスにecho関数を移動。
データ受信 ⇒ デコード ⇒ データ送信
この内データ送信はmain関数が所属するクラスで実行するため、新しくsendAllメソッドを作成。
※データ送信は全クライアントに対して実行。
全クライアントを扱っているのはmain関数のクラス。5.その他、メソッドの型や変数名等の細かい修正。
実行結果
- サーバープログラムを起動
- 複数のブラウザからlocalhostへ接続
1つのクライアントから送信すると、全てのブラウザで同じテキストが表示される。
どのクライアントからテキストを送信しても表示結果は同じ。その他
改善点
・ブラウザを閉じたときのエラー処理
「bye」を送信すると監視のループを抜けてエラーなく終了できるが、監視中にブラウザを閉じるとエラーが発生しサーバーが停止してしまう。・closeメソッドの追記
本来はソケット通信を終了する際はcloseメソッドを使用して切断するが、今回は使用していない。・デコード処理の強化
デコードの処理を甘えた為、125文字以下の通信しかできない。
126文字以上になるとwebソケットのフレームが変化するため、場合分けが必要になってくる。備考
・ポートの解放
プログラム内で指定したポートが既に使用されている場合、サーバー起動時にエラーが発生する。
1. コマンドプロンプトで>netstat -ano
を実行
2. 指定したローカルアドレスからPIDを確認
3. タスクマネージャーのプロセスからPDIのアプリケーションを確認
4. 必要ない物ならプロセスを終了してポートを開放
・ハンドシェイク
ソケット通信を行う前に、クライアントとサーバー間で合意を取る必要がある。
クライアントからサーバーへ接続要求があるなら、それに対してレスポンスを返す必要がある。
レスポンスの内容は参考ページに詳しく記述されている。・受信データのデコード
webソケットのデータにはフレームフォーマットが存在し、そのフレームに沿ってバイナリデータが送信されている。
データをデコードするには、フレーム構造を理解して1バイト毎に変換していく必要がある。
フレームフォーマットの詳細やデコード方法は参考ページに詳しく記述されている。感想
思った以上にややこしい。
ググってもなかなか出てこないし、マイナーな方法?
よく見かけるのは、サーバープログラムを「Node.js」で記述したもの。
ドットインストールの動画ではNode.jsで作成しており、レスポンスやデコード等の複雑な処理はしていなかった。
多分、Node.jsの勉強をした方が早い。Javaでも、アノテーションを利用するとデコード処理が無くても通信できるような記事も見かけたが、今回はパスした。
パスしない方が良かったかも。プログラミングの勉強をしていたはずが、いつの間にかプロトコルの勉強になっていた・・・
初学者が首を突っ込むところじゃない気がしたが、勉強になったので良し。間違えているところがあったらスマン。
気が付けば修正する。
改善すべき点もかなり多いので、可能なら更新していく。参考ページ
- 投稿日:2020-05-17T14:49:55+09:00
[Java] なんか出力に「-0.0」って表示される
きっかけは偶然に
IntelliJで気の向くままにjavaしてたら「ん?」ってなった
出力.コンソールvalue = -0.0結論
0を負の数で割ると-0.0になるっぽい
sample.javapublic class Main { public static void main(String[] args) { double value = 0; System.out.println("value = "+value/-1); System.out.printf("value = %3.1f\n",value/-5); } }出力.コンソールvalue = -0.0 value = -0.0対処
if文つけるかぁ
割る値に対してif文で0代入すれば表示は正しく出るsample.javapublic class Main { public static void main(String[] args) { double value = 0; value /= -1; System.out.println("対処前 = "+value); if(value == 0) value =0; System.out.println("対処後 = "+value); } }出力.コンソール対処前 = -0.0 対処後 = 0.0
- 投稿日:2020-05-17T13:59:34+09:00
Hotspot JVMの起動手順をちょっと調べてみる
概要
たまにJVMのソースコードを読む必要があります。どこから初めていいのかといってやはり起動の手順でしょう。この記事は実験しながらJVMの起動手順とそこにたどり着いた
libjvm.so
をちょっと調べた記録です。環境構築
作業環境では Vagrant 2.2.8 を利用して Ubuntu 18.04 にします。まずはこちらの
Vagrantfile
を用意します。VagrantfileVagrant.configure("2") do |config| config.vm.box = "ubuntu/bionic64" config.vbguest.auto_update = false config.vm.provider "virtualbox" do |vb| vb.memory = "4096" end endそして仮想マシンに登録
$ vagrant up $ vagrant sshOpenJDKをビルと
必要なパッケージをインストール
$ apt-get update $ DEBIAN_FRONTEND=noninteractive ln -fs /usr/share/zoneinfo/Asia/Tokyo /etc/localtime && apt-get install -y tzdata && dpkg-reconfigure --frontend noninteractive tzdata $ apt-get install build-essential autoconf systemtap-sdt-dev libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev libcups2-dev libfontconfig1-dev libasound2-dev unzip zip ccache gdb -y次はソースコードをダウンロード
$ cd /vagrant $ mkdir jdk && cd jdk $ git clone https://github.com/openjdk/jdk13u.git $ wget https://download.java.net/java/GA/jdk12.0.2/e482c34c86bd4bf8b56c0b35558996b9/10/GPL/openjdk-12.0.2_linux-x64_bin.tar.gz $ tar xvf openjdk-12.0.2_linux-x64_bin.tar.gzビルとを始める
$ cd jdk13u $ bash configure --enable-debug --with-jvm-variants=server --enable-dtrace --with-boot-jdk=../jdk-12.0.2 --enable-ccache $ make images2時間ぐらい待ってて、やっと成功しました。そしてビルと結果を検証しましょう。
$ export JAVA_HOME=$PWD/build/linux-x86_64-server-fastdebug/jdk $ $JAVA_HOME/bin/java -version openjdk version "13.0.3-internal" 2020-04-14 OpenJDK Runtime Environment (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u) OpenJDK 64-Bit Server VM (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u, mixed mode)起動手順のルート
DEBUG変数でもっと詳しい情報を
まずは
_JAVA_LAUNCHER_DEBUG
という環境変数を設定して、もっと詳しい起動の情報が手に入れます。$ _JAVA_LAUNCHER_DEBUG=1 $JAVA_HOME/bin/java -version ----_JAVA_LAUNCHER_DEBUG---- Launcher state: First application arg index: -1 debug:on javargs:off program name:java launcher name:openjdk javaw:off fullversion:13.0.3-internal+0-adhoc.root.jdk13u Java args: Command line args: argv[0] = /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/bin/java argv[1] = -version JRE path is /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk jvm.cfg[0] = ->-server<- jvm.cfg[1] = ->-client<- 1 micro seconds to parse jvm.cfg Default VM: server Does `/vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/lib/server/libjvm.so' exist ... yes. mustsetenv: FALSE JVM path is /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/lib/server/libjvm.so 1 micro seconds to LoadJavaVM JavaVM args: version 0x00010002, ignoreUnrecognized is JNI_FALSE, nOptions is 3 option[ 0] = '-Dsun.java.launcher.diag=true' option[ 1] = '-Dsun.java.launcher=SUN_STANDARD' option[ 2] = '-Dsun.java.launcher.pid=11021' openjdk version "13.0.3-internal" 2020-04-14 OpenJDK Runtime Environment (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u) OpenJDK 64-Bit Server VM (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u, mixed mode)いろいろな関数がプリントアウトされて、その中注目すべきなのはこちら
JVM path is /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/lib/server/libjvm.so
libjvm.so
というライブラリを利用してるみたいです。
libjvm.so
を探すソースコードに
libjvm.so
をちょっと探してみると、java_md_solinux.c:556
にdlopen(3)
が見つけた。src/java.base/unix/native/libjli/java_md_solinux.c... JLI_TraceLauncher("JVM path is %s\n", jvmpath); libjvm = dlopen(jvmpath, RTLD_NOW + RTLD_GLOBAL); if (libjvm == NULL) { ...そしてそのあど
java_md_solinux.c:609
にJNI_CreateJavaVM
というシンボルを読みました。src/java.base/unix/native/libjli/java_md_solinux.c... ifn->CreateJavaVM = (CreateJavaVM_t) dlsym(libjvm, "JNI_CreateJavaVM"); ...この
JNI_CreateJavaVM
はJVMの入り口でしょうね。
JNI_CreateJavaVM
にたどり着くまで実は
src/java.base/share/native/launcher/main.c
からちょっと読んでみると、大体の手順のわかっている。
main.c::main
java.c::JLI_Launch
java_md_solinux.c::LoadJavaVM
java_md_solinux.c::JVMInit
java_md_solinux.c::CallJavaMainInNewThread
java.c::JavaMain
java.c::InitializeJVM
jni.cpp::JNI_CreateJavaVM
gdb で検証しましょう。
$ gdb -q --args $JAVA_HOME/bin/java -version <<EOF set print thread-events off set print frame-arguments none set breakpoint pending on handle SIGSEGV nostop noprint pass break JNI_CreateJavaVM commands silent thread apply all backtrace continue end run EOF Reading symbols from /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/bin/java...(no debugging symbols found)...done. (gdb) (gdb) (gdb) (gdb) Signal Stop Print Pass to program Description SIGSEGV No No Yes Segmentation fault (gdb) Signal Stop Print Pass to program Description SIGSEGV No No Yes Segmentation fault (gdb) Function "JNI_CreateJavaVM" not defined. Breakpoint 1 (JNI_CreateJavaVM) pending. (gdb) >>>>(gdb) (gdb) Starting program: /vagrant/jdk/jdk13u/build/linux-x86_64-server-fastdebug/jdk/bin/java -version [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [Switching to Thread 0x7ffff7fea700 (LWP 11128)] Thread 2 (Thread 0x7ffff7fea700 (LWP 11128)): #0 JNI_CreateJavaVM (vm=..., penv=..., args=...) at ../../src/hotspot/share/prims/jni.cpp:4012 #1 0x00007ffff7bca1e6 in InitializeJVM (ifn=..., penv=..., pvm=...) at ../src/java.base/share/native/libjli/java.c:1539 #2 JavaMain (_args=...) at ../src/java.base/share/native/libjli/java.c:417 #3 0x00007ffff7bce239 in ThreadJavaMain (args=...) at ../src/java.base/unix/native/libjli/java_md_solinux.c:740 #4 0x00007ffff719c6db in start_thread (arg=...) at pthread_create.c:463 #5 0x00007ffff78f688f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 Thread 1 (Thread 0x7ffff7fec380 (LWP 11124)): #0 0x00007ffff719dd2d in __GI___pthread_timedjoin_ex (threadid=..., thread_return=..., abstime=..., block=...) at pthread_join_common.c:89 #1 0x00007ffff719db5c in __pthread_join (threadid=..., thread_return=...) at pthread_join.c:24 #2 0x00007ffff7bcee2d in CallJavaMainInNewThread (stack_size=..., args=...) at ../src/java.base/unix/native/libjli/java_md_solinux.c:762 #3 0x00007ffff7bcb70d in ContinueInNewThread (ifn=..., threadStackSize=..., argc=..., argv=..., mode=..., what=..., ret=...) at ../src/java.base/share/native/libjli/java.c:2367 #4 0x00007ffff7bceefb in JVMInit (ifn=..., threadStackSize=..., argc=..., argv=..., mode=..., what=..., ret=...) at ../src/java.base/unix/native/libjli/java_md_solinux.c:809 #5 0x00007ffff7bccd1b in JLI_Launch (argc=..., argv=..., jargc=..., jargv=..., appclassc=..., appclassv=..., fullversion=..., dotversion=..., pname=..., lname=..., javaargs=..., cpwildcard=..., javaw=..., ergo=...) at ../src/java.base/share/native/libjli/java.c:344 #6 0x0000555555554ad3 in main () openjdk version "13.0.3-internal" 2020-04-14 OpenJDK Runtime Environment (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u) OpenJDK 64-Bit Server VM (fastdebug build 13.0.3-internal+0-adhoc.root.jdk13u, mixed mode) [Inferior 1 (process 11124) exited normally] (gdb) quit
libjvm.so
を覗いてみる最後は
libjvm.so
を色々見てみましょ。Shared Libraryのフォーマットは elf なので、readelf
とnm
とかは活用する。$ readelf -h $JAVA_HOME/lib/server/libjvm.so ELF Header: Magic: 7f 45 4c 46 02 01 01 03 00 00 00 00 00 00 00 00 Class: ELF64 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - GNU ABI Version: 0 Type: DYN (Shared object file) Machine: Advanced Micro Devices X86-64 Version: 0x1 Entry point address: 0x303bf0 Start of program headers: 64 (bytes into file) Start of section headers: 38283720 (bytes into file) Flags: 0x0 Size of this header: 64 (bytes) Size of program headers: 56 (bytes) Number of program headers: 8 Size of section headers: 64 (bytes) Number of section headers: 37 Section header string table index: 3637のセクションにがいます。
$ readelf -S $JAVA_HOME/lib/server/libjvm.so There are 37 section headers, starting at offset 0x24829c8: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .note.gnu.build-i NOTE 0000000000000200 00000200 0000000000000024 0000000000000000 A 0 0 4 [ 2] .hash HASH 0000000000000228 00000228 0000000000001028 0000000000000004 A 4 0 8 [ 3] .gnu.hash GNU_HASH 0000000000001250 00001250 0000000000000a44 0000000000000000 A 4 0 8 [ 4] .dynsym DYNSYM 0000000000001c98 00001c98 0000000000002c70 0000000000000018 A 5 1 8 [ 5] .dynstr STRTAB 0000000000004908 00004908 0000000000001e60 0000000000000000 A 0 0 1 [ 6] .gnu.version VERSYM 0000000000006768 00006768 00000000000003b4 0000000000000002 A 4 0 2 [ 7] .gnu.version_d VERDEF 0000000000006b20 00006b20 0000000000000038 0000000000000000 A 5 2 8 [ 8] .gnu.version_r VERNEED 0000000000006b58 00006b58 0000000000000130 0000000000000000 A 5 5 8 [ 9] .rela.dyn RELA 0000000000006c88 00006c88 00000000002fa6d8 0000000000000018 A 4 0 8 [10] .rela.plt RELA 0000000000301360 00301360 0000000000001830 0000000000000018 AI 4 28 8 [11] .init PROGBITS 0000000000302b90 00302b90 0000000000000017 0000000000000000 AX 0 0 4 [12] .plt PROGBITS 0000000000302bb0 00302bb0 0000000000001030 0000000000000010 AX 0 0 16 [13] .plt.got PROGBITS 0000000000303be0 00303be0 0000000000000010 0000000000000008 AX 0 0 8 [14] .text PROGBITS 0000000000303bf0 00303bf0 00000000016df6be 0000000000000000 AX 0 0 16 [15] .fini PROGBITS 00000000019e32b0 019e32b0 0000000000000009 0000000000000000 AX 0 0 4 [16] .rodata PROGBITS 00000000019e32c0 019e32c0 0000000000204358 0000000000000000 A 0 0 32 [17] .stapsdt.base PROGBITS 0000000001be7618 01be7618 0000000000000001 0000000000000000 A 0 0 1 [18] .eh_frame_hdr PROGBITS 0000000001be761c 01be761c 000000000006646c 0000000000000000 A 0 0 4 [19] .eh_frame PROGBITS 0000000001c4da88 01c4da88 0000000000209730 0000000000000000 A 0 0 8 [20] .gcc_except_table PROGBITS 0000000001e571b8 01e571b8 00000000000000bc 0000000000000000 A 0 0 4 [21] .tdata PROGBITS 0000000002057f50 01e57f50 0000000000000008 0000000000000000 WAT 0 0 8 [22] .tbss NOBITS 0000000002057f58 01e57f58 0000000000000038 0000000000000000 WAT 0 0 8 [23] .init_array INIT_ARRAY 0000000002057f58 01e57f58 0000000000001620 0000000000000008 WA 0 0 8 [24] .fini_array FINI_ARRAY 0000000002059578 01e59578 0000000000000008 0000000000000008 WA 0 0 8 [25] .data.rel.ro PROGBITS 0000000002059580 01e59580 00000000000ef5d8 0000000000000000 WA 0 0 32 [26] .dynamic DYNAMIC 0000000002148b58 01f48b58 0000000000000240 0000000000000010 WA 5 0 8 [27] .got PROGBITS 0000000002148d98 01f48d98 0000000000000268 0000000000000008 WA 0 0 8 [28] .got.plt PROGBITS 0000000002149000 01f49000 0000000000000828 0000000000000008 WA 0 0 8 [29] .data PROGBITS 0000000002149840 01f49840 000000000003d950 0000000000000000 WA 0 0 64 [30] .bss NOBITS 00000000021871c0 01f87190 00000000000d1300 0000000000000000 WA 0 0 64 [31] .comment PROGBITS 0000000000000000 01f87190 0000000000000029 0000000000000001 MS 0 0 1 [32] .note.stapsdt NOTE 0000000000000000 01f871bc 000000000000d504 0000000000000000 0 0 4 [33] .gnu_debuglink PROGBITS 0000000000000000 01f946c0 0000000000000018 0000000000000000 0 0 4 [34] .symtab SYMTAB 0000000000000000 01f946d8 00000000001aede8 0000000000000018 35 73063 8 [35] .strtab STRTAB 0000000000000000 021434c0 000000000033f3b1 0000000000000000 0 0 1 [36] .shstrtab STRTAB 0000000000000000 02482871 0000000000000157 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), I (info), L (link order), O (extra OS processing required), G (group), T (TLS), C (compressed), x (unknown), o (OS specific), E (exclude), l (large), p (processor specific)その中気になるのは
.dynsym
セクションです、いわゆるダイナミックシンボルが格納された場所ですね。さっきのJNI_CreateJavaVM
もここにいます。$ readelf --dyn-syms $JAVA_HOME/lib/server/libjvm.so | grep JNI_CreateJavaVM 353: 0000000000f0b260 1396 FUNC GLOBAL DEFAULT 14 JNI_CreateJavaVM@@SUNWprivate_1.1
nm
なら行番号も取れます。$ nm -gl --defined-only $JAVA_HOME/lib/server/libjvm.so | grep JNI_CreateJavaVM 0000000000f0b260 T JNI_CreateJavaVM /vagrant/jdk/jdk13u/make/hotspot/../../src/hotspot/share/prims/jni.cpp:4012他の依頼されたライブラリは
ldd
使ってわかります。$ ldd $JAVA_HOME/lib/server/libjvm.so linux-vdso.so.1 (0x00007fffeef52000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f92d1fab000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f92d1d8c000) libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f92d19ee000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f92d15fd000) /lib64/ld-linux-x86-64.so.2 (0x00007f92d4408000)最後に、さっき OpenJDK をビルとしたとき
--enable-dtrace
という変数を入れたから、それらの DTrace Probe (USDT) の情報は.note.stapsdt
セクションに格納されています。$ readelf -n $JAVA_HOME/lib/server/libjvm.so | head -n 18 Displaying notes found in: .note.gnu.build-id Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: 8fc95ac20704f218afd980ae1f303abfdf0b0586 Displaying notes found in: .note.stapsdt Owner Data size Description stapsdt 0x00000050 NT_STAPSDT (SystemTap probe descriptors) Provider: hotspot Name: class__unloaded Location: 0x000000000092a226, Base: 0x0000000001be7618, Semaphore: 0x0000000000000000 Arguments: 8@%rdx -4@%eax 8@152(%rdi) 1@$0 stapsdt 0x00000050 NT_STAPSDT (SystemTap probe descriptors) Provider: hotspot Name: class__loaded Location: 0x000000000092a31b, Base: 0x0000000001be7618, Semaphore: 0x0000000000000000 Arguments: 8@%rdx -4@%eax 8@152(%rdi) 1@%silまとめ
ここまでわかったこと
java
は Launcher みたいの機能してます、いろんな初期処理してからlibjvm.so
に行きます。
- その Launcher の変数は
_JAVA_LAUNCHER_DEBUG
で詳しく表示できます。libjvm.so
の入り口はJNI_CreateJavaVM
で、全てはここで始めます。
- 投稿日:2020-05-17T08:30:13+09:00
AtCoder Beginner Contest 167 の C問題(Java)
はじめに
AtCoder Beginner Contest 167
の
C問題(C-Skill Up)
の
解説動画を見てない。または見たが理解できたか心配という方向けの記事です。私は動画の解説を見ましたが、整理するためにまとめてみました。
解説動画も非常にわかりやすい内容になっていますので、
そちらもぜひ参考にして下さい。
C問題は21:30〜42:00あたりです。解説動画はこちら。1〜Nの数字の全ての組み合わせを試す方法の解説になります。
問題
問題文を要約すると、下記のようになるかと思います。
高橋くんが学びたいアルゴリズムがM個あって、
書店にはアルゴリズム の参考書がN個ある。高橋くんには全ての参考書を買う予算があるが、
出来れば効率よく学び最小限どの出費で抑えたい。どの順番で参考書を買って読むと費用が一番抑えられるか。
かかる費用の最小値を求めよ。解き方
N冊ある参考書を購入する組み合わせ全てを試します
N冊ある参考書を購入する組み合わせは「1 << N」個あります。
<< はシフト演算です。 「1 << N」は1を左にN個シフト演算するという意味になります。
シフト演算は2進数で考えます。
<2進数>
0: 0000 1: 0001 2: 0010 3: 0011 4: 0100 5: 0101 6: 0110 7: 0111 8: 1000例: 1 << 3
0001を左に3つシフトするという意味なので、
0001000となります。よって10進数では「8」となります。よって、N冊のNが「3」であれば、組み合わせは「8パターン」となります。
なぜそうなるか?
上の2進数の0〜7を見てください。
右から3桁に注目すると0〜7で全てのパターンが網羅されているのがわかると思います。この各桁を各参考書を買うかどうかとして考えます。
例:参考書が3冊あった場合
000 ー>1冊も買わないケース 001 ー>1番目の参考書を買うが、2番目、3番目は買わないケース 010 ー>2番目の参考書を買うが、1番目、3番目は買わないケース 011 ー>1番目と2番目の参考書を買うが、3番目は買わないケース 100 ー>3番目の参考書を買うが、1番目、2番目は買わないケース 101 ー>1番目と3番目の参考書を買うが、2番目は買わないケース 110 ー>2番目と3番目の参考書を買うが、1番目は買わないケース 111 ー>全部買うケースn=3の場合に8パターンループするソースコード
class Main { public static void main(String[] args) { int patternCount = 1 << 3; System.out.printf("patternCount=%d\n", patternCount); for (int i = 0; i < patternCount; i++) { System.out.println(Integer.toBinaryString(i)); } } }↑8パターンループするので、単に8回ループすればOKです。
0〜7を2進数で出力してみました。出力結果patternCount=8 0 1 10 11 100 101 110 111各パターンで買う本、買わない本をみていく
2進数で表現した場合の各桁が1かどうかで買う本、買わない本を見ていきます。
001であれば、1番目の参考書を買うが、2番目、3番目は買わないケース
110であれば、2番目と3番目の参考書を買うが、1番目は買わないケースといった感じですよね。
つまり、
1番目の本を買う場合は、 右から数えて1番目の数字が1
2番目の本を買う場合は、 右から数えて2番目の数字が1
3番目の本を買う場合は、 右から数えて3番目の数字が1ってことですね。
2進数で指定した桁の数字が1かどうかを調べるには「&」演算子を使います。
「&」演算子は、2進数の表現で各桁の数値が両方とも1だった場合にだけ1にします。
わかりやすく2進数で表現してみます。
1111 & 0001 -> 0001
1111 & 1001 -> 1001
1000 & 1001 -> 1000このように左右の2進数の各桁を比較し両方とも1の場合だけその桁を1とする演算です。
これを利用し、
- 1番目の本を買ったかどうかを調べるには、 001 で & した結果が001かどうか調べる
- 2番目の本を買ったかどうかを調べるには、 010 で & した結果が010かどうか調べる
- 3番目の本を買ったかどうかを調べるには、 100 で & した結果が100かどうか調べる
となります。
class Main { public static void main(String[] args) { int n = 3; System.out.printf("n=%d\n", n); int patternCount = 1 << n; System.out.printf("patternCount=%d\n", patternCount); for (int i = 0; i < patternCount; i++) { System.out.println(Integer.toBinaryString(i)); for (int j = 0; j < n; j++) { if ( (i & 1<<j) == 1<<j) { System.out.printf(" %d番目の参考書を買う\n", j + 1); } } } } }↑このように2進数の各桁が1かどうかを調べます。
出力結果は下記のようになります。n=3 patternCount=8 0 1 1番目の参考書を買う 10 2番目の参考書を買う 11 1番目の参考書を買う 2番目の参考書を買う 100 3番目の参考書を買う 101 1番目の参考書を買う 3番目の参考書を買う 110 2番目の参考書を買う 3番目の参考書を買う 111 1番目の参考書を買う 2番目の参考書を買う 3番目の参考書を買う購入費の最小値を探す
さて、これでこの問題を解く土台ができました。
全てのパターンを試して、購入費が一番小さくなるのを調べます。下記のソースコードでC問題がACとなりました。
import java.util.*; class Main { final static int INF = 1001001001; public static void main(String[] args) { Scanner sc = new Scanner(System.in); // 入力データを変数に格納する int n = sc.nextInt(); int m = sc.nextInt(); int x = sc.nextInt(); int[][] a = new int[12][12]; int[] c = new int[n]; for (int i = 0; i < n; i++) { c[i] = sc.nextInt(); for (int j = 0; j < m; j++) { a[i][j] = sc.nextInt(); } } sc.close(); // 回答=コストの最小値なので最小値比較のために大きな数値で初期化しておく。 int ans = INF; // 全ての組み合わせパターンをチェックする for (int s = 0; s < 1<<n; s++) { int cost = 0; // このs番目の組み合わせのコスト int[] d = new int[m]; // このs番目の組み合わせでの各アルゴリズムの理解度 // 2進数で各ビットが1になっているかどうか調べる for (int i = 0; i < n; i++) { // 右からi番目のビットが1になっていれば、購入する参考書としてコストとアルゴリズムを加算する if ( (s & 1<<i) == 1<<i) { // コストを足す cost += c[i]; // 各アルゴリズムの理解度に足す for (int j = 0; j < m; j++) { d[j] += a[i][j]; } } } // 全てのアルゴリズムの理解度がxを超えているかチェックする boolean ok = true; for (int j = 0; j < m; j++) { if (d[j] < x) ok = false; // 1つでもx未満があれば。 } // チェックOKであれば、そのコストが最小値を更新するかどうか。 if (ok) ans = Math.min(ans, cost); } // 全てのアルゴリズムの理解度がxを超えるパターンが1つもない場合はansがINFのままになってる if (ans == INF) System.out.println(-1); else System.out.println(ans); } }おわりに
いかがでしたでしょうか?
1〜Nの数字の全ての組み合わせを試す
といった問題に出会ったときに、この解き方が使えるのではないかと思います。
おわり。
- 投稿日:2020-05-17T04:32:04+09:00
【Kotlin】KClassからClassを取得する3通りの方法
KClass
にはClass
を返すプロパティとして以下の3種類が定義されています。
.java
.javaObjectType
.javaPrimitiveType
Kotlin
のInt
型を例にすると、返却内容は以下のようになります。
.java
.javaObjectType
.javaPrimitiveType
Int::class
int.class
Integer.class
int.class
Integer::class
Integer.class
Integer.class
int.class
その他 *.class
*.class
null
どんな時に気にすべきか
プリミティブ型とラッパー型を区別する必要の有無によって使い分ける必要が出るでしょう。
また、「Class
を要求するような実装でプリミティブ型に対応していない」というパターンを踏んだことが有るので、そういった場合には.javaObjectType
呼び出しを行う必要が有ります。参考にさせて頂いた記事
- 投稿日:2020-05-17T01:16:02+09:00
javaでアルゴリズム入門 - 探索編(幅優先探索)
記事の概要
自分の勉強兼メモでまとめている記事シリーズです。第三弾。
こちらの記事の続きです。
javaでアルゴリズム入門 - 探索編(深さ優先探索)
今回の記事では
- 幅優先探索
について勉強します。
幅優先探索
BFS(breadth-first search)とも言うらしいです。(breadth:幅)
これも勉強しながら書いていきますね。木に見立てた深さ優先探索との違いなどはこちらの記事「javaでアルゴリズム入門 - 探索編(深さ優先探索)」をご覧ください。迷路の最短経路を求める問題などに有効だそうですね。
実装の方法としてはキューというデータ構造を使うようです。
深さ優先探索では「先入れ後出し」のスタック、
幅優先探索では「先入れ先出し」のキューを用いることを覚えておきましょう。また、深さ優先探索は再帰でも実装できましたが、キューの順番で処理をするのは再帰だと無理or難しいようです。queue使うか。。勉強の初めにあんまり新しいライブラリというか型とか覚えたくないんだけどな。。。すぐに使いこなせる気がしないから。。
ちなみにスタックやキューは基本情報技術者試験でも出てくるので大丈夫だとは思いますが、わからないよ!という方はこちらの記事「スタックとキューを極める! 〜 考え方と使い所を特集 〜」が分かりやすそうですので勉強しておきましょう。
例題はとりあえず分かりやすいように前回DFSで実装したarc031-bをやってみようかと思ったのですがなんだかBFSには合わないかも、ということで別の例題にしようと思います。
例:AtCoder - agc033-a「Darker and Darker」
問題文・入力例などはここをクリックして表示
※できるだけ問題リンクを参照してください
(セクション開始)
【問題文】
縦 H 行、横 W 列の白黒に塗られたマス目が与えられます。 マス目の状態は A11 からAHW の HW 個の文字で表されており、 上から i 行目、左から j列目にあるマスが黒色のとき Aij は#、 上から i 行目、左から j 列目にあるマスが白色のとき Aij は . となっています。すべてのマスが黒色になるまで、以下の操作を繰り返し行います。
辺を共有して隣接するマスの中に、黒色のマスが一つ以上存在するような白色のマスすべてが黒色になる。
何回の操作を行うことになるか求めてください。 ただし、最初に与えられるマス目には少なくとも 1 つ黒色のマスが存在します。【制約】
1≦H,W≦1000
Aij は # または .
与えられるマス目には少なくとも 1 つ黒色のマスが存在する。【入力】
入力は以下の形式で標準入力から与えられる。H W A11 A12 ... A1W : AH1 AH2 ... AHW【出力】
行われる操作の回数を出力せよ。(セクション終了)
【回答例】
main.javaimport java.util.ArrayDeque; import java.util.Deque; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); // height int h = sc.nextInt(); // width int w = sc.nextInt(); // マス目の状態 char[][] masu = new char[h][w]; // BFSのQueueを準備 Deque<XY> q = new ArrayDeque<>(); // 入力を受け取る & '#'のマスをEnQueue for (int i = 0; i < h; i++) { masu[i] = sc.next().toCharArray(); for (int j = 0; j < w; j++) { if ('#' == masu[i][j]) { q.add(new XY(i, j, 0)); } } } // ans int max_depth = 0; // BFS主処理 for (;;) { // 無限ループ開始 // 終了条件 if (q.size() < 1) { break; } // queueからdequeue // pollメソッドはqueueから取り出し & queueから削除 XY xy = q.poll(); int x = xy.x; int y = xy.y; int depth = xy.depth; // 操作の回数を記録 if (depth >= max_depth) { max_depth = depth; } // 現在の位置の周囲を黒にする if (x + 1 < w && masu[y][x + 1] == '.') { // 右マス masu[y][x + 1] = '#'; q.add(new XY(y, x + 1, depth + 1)); } if (x - 1 >= 0 && masu[y][x - 1] == '.') { // 左マス masu[y][x - 1] = '#'; q.add(new XY(y, x - 1, depth + 1)); } if (y + 1 < h && masu[y + 1][x] == '.') { // 下マス masu[y + 1][x] = '#'; q.add(new XY(y + 1, x, depth + 1)); } if (y - 1 >= 0 && masu[y - 1][x] == '.') { // 上マス masu[y - 1][x] = '#'; q.add(new XY(y - 1, x, depth + 1)); } } System.out.println(max_depth); } } class XY { int y; int x; int depth; XY(int y, int x, int d) { this.x = x; this.y = y; depth = d; } }こんな感じです。
方針としては以下の感じです。
- 初期状態で黒のマスの情報をQueueに入れる。(座標、何回目で黒になったか(=深さ))
- Queueから情報を取り出して、その座標の上下左右のマスを探索する。白だった場合、黒にして深さをインクリメントし、Queueに格納する。黒だったらなにもしない。
- Queueからデータがなくなるまで(≒全部黒になる)繰り返し。
この順番だと必ず深さ0,1,2...って順番にQueueに入るから黒になったときの深さがそのマスの最小の深さになるから、一回黒になったらOKというところがありがたいです。
ちなみにこれは幅優先探索の亜種というか、スタート地点が複数あるタイプのBFSに分類?されるみたいですね。
幅優先探索というよりはQueueの使い方の勉強になりました。笑
Dequeのpollメソッドはよく使うと思うので是非覚えたいところですね。今回も一応幅優先探索で実装した際の動きを視覚的にみようかと思いますが前回の記事で懲りたので(というかわかるでしょ?と思ったのでw)やめます。
一応わかりやすくコメントとかもコード内に書いてるので是非追ってみてください。とりあえずこれが書ければ応用効きそうじゃない?
あとは問題演習ですね。理論を学んだら繰り返し実践して自分に定着していきましょう。数をこなしていきたい。
今回はここまでです。
次回はbit全探索について触れようと思います。
お楽しみに!
- 投稿日:2020-05-17T01:14:42+09:00
java(クラス)
- 投稿日:2020-05-17T01:14:42+09:00
java(クラスとインスタンス)
定数フィールドの宣言
public class Matango { int hp; //int level = 10; //フィールド final int LEVEL = 10; //定数フィールド }値を書き換えることができないようにする定数フィールド
final
を付けて名前を大文字
にする(LEVEL)thisは省略しない
public class Hero { String name; //名前の宣言 int hp; //HPの宣言 public void sleep() { this.hp = 100; System.out.println(this.name + "は、眠って回復した"); }
this.
は省略しても動作する
ローカル変数や引数にも同じhpがあるとそちらが優先されてしまう可能性がある
フィールドを用いる時はthis.
をつけるようにするインスタンスの生成
public class Main { public static void main(String[] args) { //1、勇者を生成 Hero h = new Hero(); } }クラス名(Hero) 変数名(h) = new クラス名(Hero)();
フィールドへの値の代入
public class Main { public static void main(String[] args) { //1、勇者を生成 Hero h = new Hero(); //2、フィールドに初期値をセット h.name = "勇者"; h.hp = 100; System.out.println("勇者" + h.name + "を生み出しました!"); } }変数名.(h.) フィールド名(name) = 値("勇者");