20200517のJavaに関する記事は16件です。

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.ino
int 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.js
var 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.js
const 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へアクセスするとこんな感じのグラフが表示されます。
ダウンロード.png
光センサーの上で手で光を遮ったりしたグラフになります。光を遮ると光センサーの抵抗値が上がるので電圧も上がります。
横軸がなぜか均等にならないのですが、気が向いたら改善します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.gradle
    compile 'org.springframework.boot:spring-boot-starter-data-jpa' // ← 追加(Spring Data JPA)

と言う形で、implementation を、compile に変更するとOK。
そういうものなのかな・・・(gradle初体験中)

これにより、無事データも表示されました。

テンプレートは当然のこと、コントローラを修正して保存すると、自動でビルドが動く感じですね。

これで基本の枠組みができました。

なお、この後、docker-compose.yml をちょっと修正。
今作業しているフォルダにJavaのソースも置くようにしたいと思い、volumes: の記述を追加しました。

docker-compose.yml
version: '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"

スクリーンショット 2020-05-14 8.58.02.png

画面デザイン

これでいわゆる普通のサーバアプリを開発する環境ができたわけですが、フロント側をどうするか。
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>

ほのか.png

これで進めようと思ったのですが・・・

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-latest

Debian または 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/151403

https://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/

今後は、コンテンツを整えながら、データ検索等の仕組みを作っていきます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaのfor文

for文

while文と一緒でfor文も繰り返し処理の1つです。
繰り返し処理の流れとしては
①変数の初期化
②条件式
③繰り返す処理
④変数の更新
そこからまた
②条件式
③繰り返す処理
④変数の更新
と、繰り返します。
for文

Main.java
for (変数の初期化;条件式;変数の更新;) {
  System.out.println(繰り返す処理); 
}

【例】

Main.java
for (int x = 1;x <= 10;x++;) {
  System.out.println(x); 
}

上記ように記述すると10回処理を繰り返します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.java
import 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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaのwhile文

while文

while文は一定の処理を自動で繰り返し処理をしたいときに使います。
例えば、1~100までの数字をコンソールに出力したい場合、System.out.println()を100回書くのはコードが冗長になってしまいます。そこで、while文を使います。

Main.java
while (条件) {
  繰り返す処理;
}

上記のwhile文を使えば繰り返し処理を実行できます。
【例】

Main.java
int x = 3;
  while (x <= 3) [
  System.out.println(x + "位");
  x++;
}

上記はx = 3で変数xを定義しています。x++;はxに1を追加していきます。
変数に1を足していきますので、そうすると4回目の繰り返しでwhileの条件がfalseになり、繰り返し処理が終了します。

【例】

Main.java
int x =5;
  while (x > 0) [
  System.out.println(x);
  x--;
}

上記はが0より大きい場合に繰り返し処理をします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaのwhile文とfor文

while文

while文は一定の処理を自動で繰り返し処理をしたいときに使います。
例えば、1~100までの数字をコンソールに出力したい場合、System.out.println()を100回書くのはコードが冗長になってしまいます。そこで、while文を使います。
繰り返し処理の流れとしては
①変数の初期化
②条件式
③繰り返す処理
④変数の更新
そこからまた
②条件式
③繰り返す処理
④変数の更新
と、繰り返します。

Main.java
while (条件) {
  繰り返す処理;
}

上記のwhile文を使えば繰り返し処理を実行できます。
【例】

Main.java
int x = 3;
  while (x <= 3) [
  System.out.println(x + "位");
  x++;
}

上記はx = 3で変数xを定義しています。x++;はxに1を追加していきます。
変数に1を足していきますので、そうすると4回目の繰り返しでwhileの条件がfalseになり、繰り返し処理が終了します。

【例】

Main.java
int x =5;
  while (x > 0) [
  System.out.println(x);
  x--;
}

上記はが0より大きい場合に繰り返し処理をします。

for文

while文と一緒でfor文も繰り返し処理の1つです。
繰り返し処理の流れとしてはwhile文と一緒で
①変数の初期化
②条件式
③繰り返す処理
④変数の更新
そこからまた
②条件式
③繰り返す処理
④変数の更新
と、繰り返します。
for文

Main.java
for (変数の初期化;条件式;変数の更新) {
  System.out.println(繰り返す処理); 
}

【例】

Main.java
for (int x = 1;x <= 10;x++) {
  System.out.println(x); 
}

上記ように記述すると10回処理を繰り返します。
使うのであればfor文の方が使いやすいですね。

continue

繰り返し処理を終了するにはbreakを使いますが、continueはその周の処理だけをスキップして、次の周を実行します。
【例】

Main.java
for (int x = 1;x <= 10;x++) {
  if(x%5==0) {
  continue;
  }
  System.out.println(x); 
}

上記のように記述すると、5の倍数の周のループを終了し、次のループを実行します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaとJavaScriptでwebブラウザとのソケット通信

背景

ネット上で多人数同時通信のものを作成するため、ソケット通信について実践したことを記述。
チャットやオンラインゲームのような、複数でのリアルタイム通信を希望。

「webブラウザ」で実行できるようなプログラムを「Java」で組みたかったのだが、ググってもあまり出てこないので記述しておく。
(知識不足でネットワークプログラミングという言葉も知らなかったため、ググり方に問題があるかも…)

データの読み書き等の超基本的なところからスタート。

ソケット通信

通信方法の1種。
サーバーとクライアントに対して、HTTPのgetアクセスやpostアクセスのように単一方向への通信ではなく、リアルタイムでの双方向通信が可能。
⇒ サーバープログラムとクライアントプログラムの2つが必要。

詳しくはググってほしい。

実行環境

  • windowds
  • eclipse (Tomcat8.0)
  • Google Chrome

言語

サーバープログラム:Java
クライアントプログラム:JavaScript

成果物

  1. webブラウザで動くチャットアプリ

開発手順

  1. クライアント側のプログラムを組む
  2. サーバー側のプログラムを組む
  3. クライアントプログラムからサーバーへアクセス
  4. サーバーからの応答があればOK

実践項目

  1. クライアントエコーによるソケット通信
  2. 単一クライアントとサーバーのチャットアプリ
  3. 複数クライアントとサーバーのチャットアプリ
  4. 複数クライアントでのじゃんけんシステム

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(・・・);

実行結果

キャプチャ.JPG
キャプチャ2.JPG
キャプチャ3.JPG

送信した内容がそのまま架空サーバーから送り返されて表示される。

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」が送信されるとループを抜け受信終了

実行結果

  1. サーバープログラムを起動
  2. ポート番号を指定し、localhostへ接続

キャプチャ3.JPG

※クライアントプログラムのアドレスを変更しておく(今回は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.その他、メソッドの型や変数名等の細かい修正。

実行結果

  1. サーバープログラムを起動
  2. 複数のブラウザからlocalhostへ接続

キャプチャ5.JPG

1つのクライアントから送信すると、全てのブラウザで同じテキストが表示される。
どのクライアントからテキストを送信しても表示結果は同じ。

その他

改善点

・ブラウザを閉じたときのエラー処理
「bye」を送信すると監視のループを抜けてエラーなく終了できるが、監視中にブラウザを閉じるとエラーが発生しサーバーが停止してしまう。

・closeメソッドの追記
本来はソケット通信を終了する際はcloseメソッドを使用して切断するが、今回は使用していない。

・デコード処理の強化
デコードの処理を甘えた為、125文字以下の通信しかできない。
126文字以上になるとwebソケットのフレームが変化するため、場合分けが必要になってくる。

・セキュリティ関連の強化
今回は何も対策しておらずセキュリティがガバガバなので、送受信の方法を考える必要がある。

備考

・ポートの解放
プログラム内で指定したポートが既に使用されている場合、サーバー起動時にエラーが発生する。
1. コマンドプロンプトで>netstat -anoを実行
2. 指定したローカルアドレスからPIDを確認
3. タスクマネージャーのプロセスからPDIのアプリケーションを確認
4. 必要ない物ならプロセスを終了してポートを開放
キャプチャ6.JPG

・ハンドシェイク
ソケット通信を行う前に、クライアントとサーバー間で合意を取る必要がある。
クライアントからサーバーへ接続要求があるなら、それに対してレスポンスを返す必要がある。
レスポンスの内容は参考ページに詳しく記述されている。

・受信データのデコード
webソケットのデータにはフレームフォーマットが存在し、そのフレームに沿ってバイナリデータが送信されている。
データをデコードするには、フレーム構造を理解して1バイト毎に変換していく必要がある。
フレームフォーマットの詳細やデコード方法は参考ページに詳しく記述されている。

感想

思った以上にややこしい。

ググってもなかなか出てこないし、マイナーな方法?
よく見かけるのは、サーバープログラムを「Node.js」で記述したもの。
ドットインストールの動画ではNode.jsで作成しており、レスポンスやデコード等の複雑な処理はしていなかった。
多分、Node.jsの勉強をした方が早い。

Javaでも、アノテーションを利用するとデコード処理が無くても通信できるような記事も見かけたが、今回はパスした。
パスしない方が良かったかも。

プログラミングの勉強をしていたはずが、いつの間にかプロトコルの勉強になっていた・・・
初学者が首を突っ込むところじゃない気がしたが、勉強になったので良し。

間違えているところがあったらスマン。
気が付けば修正する。
改善すべき点もかなり多いので、可能なら更新していく。

参考ページ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaとJavaScriptでwebブラウザのソケット通信

背景

ネット上で多人数同時通信のものを作成するため、ソケット通信について実践したことを記述。
チャットやオンラインゲームのような、複数でのリアルタイム通信を希望。

「webブラウザ」で実行できるようなプログラムを「Java」で組みたかったのだが、ググってもあまり出てこないので記述しておく。
(知識不足でネットワークプログラミングという言葉も知らなかったため、ググり方に問題があるかも…)

データの読み書き等の超基本的なところからスタート。

ソケット通信

通信方法の1種。
サーバーとクライアントに対して、HTTPのgetアクセスやpostアクセスのように単一方向への通信ではなく、リアルタイムでの双方向通信が可能。
⇒ サーバープログラムとクライアントプログラムの2つが必要。

詳しくはググってほしい。

実行環境

  • windowds
  • eclipse (Tomcat8.0)
  • Google Chrome

言語

サーバープログラム:Java
クライアントプログラム:JavaScript

成果物

  1. webブラウザで動くチャットアプリ

開発手順

  1. クライアント側のプログラムを組む
  2. サーバー側のプログラムを組む
  3. クライアントプログラムからサーバーへアクセス
  4. サーバーからの応答があればOK

実践項目

  1. クライアントエコーによるソケット通信
  2. 単一クライアントとサーバーのチャットアプリ
  3. 複数クライアントとサーバーのチャットアプリ
  4. 複数クライアントでのじゃんけんシステム

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(・・・);

実行結果

キャプチャ.JPG
キャプチャ2.JPG
キャプチャ3.JPG

送信した内容がそのまま架空サーバーから送り返されて表示される。

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」が送信されるとループを抜け受信終了

実行結果

  1. サーバープログラムを起動
  2. ポート番号を指定し、localhostへ接続

キャプチャ3.JPG

※クライアントプログラムのアドレスを変更しておく(今回は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.その他、メソッドの型や変数名等の細かい修正。

実行結果

  1. サーバープログラムを起動
  2. 複数のブラウザからlocalhostへ接続

キャプチャ5.JPG

1つのクライアントから送信すると、全てのブラウザで同じテキストが表示される。
どのクライアントからテキストを送信しても表示結果は同じ。

その他

改善点

・ブラウザを閉じたときのエラー処理
「bye」を送信すると監視のループを抜けてエラーなく終了できるが、監視中にブラウザを閉じるとエラーが発生しサーバーが停止してしまう。

・closeメソッドの追記
本来はソケット通信を終了する際はcloseメソッドを使用して切断するが、今回は使用していない。

・デコード処理の強化
デコードの処理を甘えた為、125文字以下の通信しかできない。
126文字以上になるとwebソケットのフレームが変化するため、場合分けが必要になってくる。

備考

・ポートの解放
プログラム内で指定したポートが既に使用されている場合、サーバー起動時にエラーが発生する。
1. コマンドプロンプトで>netstat -anoを実行
2. 指定したローカルアドレスからPIDを確認
3. タスクマネージャーのプロセスからPDIのアプリケーションを確認
4. 必要ない物ならプロセスを終了してポートを開放
キャプチャ6.JPG

・ハンドシェイク
ソケット通信を行う前に、クライアントとサーバー間で合意を取る必要がある。
クライアントからサーバーへ接続要求があるなら、それに対してレスポンスを返す必要がある。
レスポンスの内容は参考ページに詳しく記述されている。

・受信データのデコード
webソケットのデータにはフレームフォーマットが存在し、そのフレームに沿ってバイナリデータが送信されている。
データをデコードするには、フレーム構造を理解して1バイト毎に変換していく必要がある。
フレームフォーマットの詳細やデコード方法は参考ページに詳しく記述されている。

感想

思った以上にややこしい。

ググってもなかなか出てこないし、マイナーな方法?
よく見かけるのは、サーバープログラムを「Node.js」で記述したもの。
ドットインストールの動画ではNode.jsで作成しており、レスポンスやデコード等の複雑な処理はしていなかった。
多分、Node.jsの勉強をした方が早い。

Javaでも、アノテーションを利用するとデコード処理が無くても通信できるような記事も見かけたが、今回はパスした。
パスしない方が良かったかも。

プログラミングの勉強をしていたはずが、いつの間にかプロトコルの勉強になっていた・・・
初学者が首を突っ込むところじゃない気がしたが、勉強になったので良し。

間違えているところがあったらスマン。
気が付けば修正する。
改善すべき点もかなり多いので、可能なら更新していく。

参考ページ

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

encoding

Hellow world

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Java] なんか出力に「-0.0」って表示される

きっかけは偶然に

IntelliJで気の向くままにjavaしてたら「ん?」ってなった

出力.コンソール
value = -0.0

結論

0を負の数で割ると-0.0になるっぽい

sample.java
public 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.java
public 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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Hotspot JVMの起動手順をちょっと調べてみる

概要

たまにJVMのソースコードを読む必要があります。どこから初めていいのかといってやはり起動の手順でしょう。この記事は実験しながらJVMの起動手順とそこにたどり着いたlibjvm.soをちょっと調べた記録です。

環境構築

作業環境では Vagrant 2.2.8 を利用して Ubuntu 18.04 にします。まずはこちらのVagrantfileを用意します。

Vagrantfile
Vagrant.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 ssh

OpenJDKをビルと

必要なパッケージをインストール

$ 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 images

2時間ぐらい待ってて、やっと成功しました。そしてビルと結果を検証しましょう。

$ 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:556dlopen(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:609JNI_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からちょっと読んでみると、大体の手順のわかっている。

  1. main.c::main
  2. java.c::JLI_Launch
  3. java_md_solinux.c::LoadJavaVM
  4. java_md_solinux.c::JVMInit
  5. java_md_solinux.c::CallJavaMainInNewThread
  6. java.c::JavaMain
  7. java.c::InitializeJVM
  8. 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 なので、readelfnmとかは活用する。

$ 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: 36

37のセクションにがいます。

$ 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で、全てはここで始めます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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の数字の全ての組み合わせを試す

といった問題に出会ったときに、この解き方が使えるのではないかと思います。

おわり。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin】KClassからClassを取得する3通りの方法

KClassにはClassを返すプロパティとして以下の3種類が定義されています。

  • .java
  • .javaObjectType
  • .javaPrimitiveType

KotlinInt型を例にすると、返却内容は以下のようになります。

.java .javaObjectType .javaPrimitiveType
Int::class int.class Integer.class int.class
Integer::class Integer.class Integer.class int.class
その他 *.class *.class null

どんな時に気にすべきか

プリミティブ型とラッパー型を区別する必要の有無によって使い分ける必要が出るでしょう。
また、「Classを要求するような実装でプリミティブ型に対応していない」というパターンを踏んだことが有るので、そういった場合には.javaObjectType呼び出しを行う必要が有ります。

参考にさせて頂いた記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.java
import 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全探索について触れようと思います。
お楽しみに!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

java(クラス)

フィールドの宣言

public class Matango {
    int hp;
    //int level = 10;
    final int LEVEL = 10;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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) = 値("勇者");

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む