java【文字列】
プログラミングにおいて、文字列を操作出来るようになるとかなり出来ることの幅が広がると言われるほど文字列を扱うことは重要である
そのことを踏まえて、javaで文字列を扱うとはどういうことかまとめてみる
まず第一に、扱う文字列をどう定義するか。
1.Stringクラスを使う
javaでは文字列を扱うにはまずStringクラスを使う
クラスというぐらいなのでnewを用いた方法で定義可能である
String str = new String("hello,world"); System.out.println(str);//print -> hello,world
上記のコードではstrという参照型変数にStringオブジェクトが代入され
その動作によりStringオブジェクトの生成が完了します。
更に、Stringクラスには文字列を操作するための様々なメソッドが用意されており、特に良く使う物をあげます
メソッド | 機能 |
---|---|
length | 文字列の長さを返す |
equals | 引数の文字列と同じ内容の文字列か調べる |
charAt | 指定したインデックスの文字列を文字として切り出す |
indexOf | 引数の文字を検索する |
split | 文字列を引数の文字列で区切って配列にする |
contains | 引数の文字を含むか検索 |
substring | 部分文字列を取得 |
compareTo | どちらが辞書的に大きいか比較 |
ここで示したメソッドだけでも大抵のことが出来ますが
逆に、Stringクラスに定義されているメソッド以外のことは出来ません
もしStringクラスに含まれていない機能を扱いたいのなら独自のクラスを定義する必要があります
しかし、実際はStringクラスに含まれるメソッドでほぼすべて実現できるのでそれ以外の機能が必要な場合はそれらを見なおして必要性を考えてください
そして設計上Stringクラスは読み込み専用のクラスなので、Stringオブジェクトそのものを変更することは出来ません
もし変更したい場合は「StringBuilderクラス」を使用します
2.文字列操作の使用例
StringBuilderクラスを説明する前に、さっき示した文字列の具体例
public class Test{ public static void main(String... args){ String str = new String("<hello,world>");//これは推奨されない int start = str.indexOf("<"); //indexOfは存在するときはそのインデックスを返す //存在しないなら−1を返す if(0 <= start){ start++;//substring使用時に最初の<が含まれてしまうので+1 int end = str.indexOf(">"); if(0 <= end){ //部分文字列の切り出し System.out.println(str.substring(start,end)); } } } }
3.文字列リテラル
実は今まで紹介してきた文字列の定義方法には問題があります
String str = new String("hello,world");
という定義方法では"hello,world"を生成する為に一度Stringオブジェクトが生成され
それをもう一度参照型変数strにStringオブジェクトを生成して代入しているからです。
つまり一度の生成のつもりが2回生成していることになります
これはとても非効率なので、もっと簡単に一度で済ませてしまう方法があります
String str = "hello,world";
このように生成したい文字列をダブルクォーテーションで囲むことで文字列リテラルとして扱われ
Stringオブジェクトの生成は一回だけです
4.StringBuilderクラス
1.Stringクラスにも書いた様にStringクラスは読み込み専用でオブジェクトそのものを変更できません
正確には、オブジェクトを変更できるメソッドは存在しますが、変更のたびにオブジェクトを生成するのでとても非効率です
そこで効率良く文字列を変更可能なクラスがStringBuilderクラスです
String -> StringBuidlerの変換
String str = "hello"; StringBuilder sb = new StringBuilder(str);
これで指定した文字列をStringBuilderに定義できます
またStringBuilderクラスのオブジェクトを生成するときには引数を指定しなくても大丈夫です
更にはオブジェクトそのものを変更出来るのでStringクラスを何回も生成する必要が無いためループ内で文字列を操作する場合はとても効率的です
StringBuilderクラスにはStringクラス同様いろんなメソッドが存在しますがよく使うものをあげます
メソッド | 機能 |
---|---|
append | 指定した文字列を末尾に追加 |
indexOf | Stringクラス同様 |
insert | 指定した文字列を指定位置に挿入 |
replace | 指定した文字列を置換する |
substring | Stringクラス同様 |
toString | Stringオブジェクトに変換する |
そしてStringクラスとStringBuilderクラスはCharSequenceという共通のインターフェースを実装しているので以下のメソッドはどちらのクラスでも共通します
メソッド | 機能 |
---|---|
charAt | 指定インデックスの文字の取得 |
length | 文字列の長さ取得 |
subSequence | 部分文字列の取得 |
5.文字列の比較
文字列を比較することで主に初心者の多いミスですが「==」や「!=」と言った演算子で比較してしまうこと
特に==を用いた比較です
String str1 = "hello"; String str2 = new StringBuilder("hello").toString(); str1 == str2//これは真にはならない
str1 == str2が真にならないのには大きな理由があります
それは==演算子では同一オブジェクトへの参照であるかを判定しているため
異なる文字列オブジェクトのstr1,str2では必ず偽になります
また異なる文字列オブジェクトを比較するなら通常equalsを用いて行われます
その場合内容自体はおなじなので真が返ってくるというわけです
なので一般的に文字列オブジェクトの比較では、equalsメソッドを使用し比較します
しかし、ここまでの方法は文字列オブジェクト、あくまでもStringオブジェクト同士の比較だけです
実はStringBuilderクラス同士の比較ではequalsを使っても偽になるため
StringBuilder同士ではtoStringで変換してからcontentEqualsを使う
これで大体の文字列の扱いに関する基礎的なことは大丈夫だと思う
javaにおけるオブジェクトの同一性と等値性
これは大抵のコンパイルを必要とする言語でよくある?やつ
javaではString型の比較で==演算子を使うと正しく評価されないという
初心者向けの入門書によく書いているあれ
ざっくり言えば
==は同一性を指す
つまりオブジェクトが同じかどうか
じゃあ、等値性ってなによってなるけど
これもざっくり答えると
equalsは等値性を指す
等値性は==ほど厳密ではなくニュアンスでいうなら「意味的に正しい」
String hello = "hello";
String world = "hello";
例えば上のような奇妙なコードがあったとしよう
これを==で評価するとStringクラスのhelloとworldという同じではないオブジェクトなので偽になる
しかしequalsメソッドを用いるとオブジェクトは違っても2つの参照型変数は同じ値を格納しているので真となる
これがオブジェクトの同一性と等値性
文字列以外でequalsを使って評価したいならinstanceofとか使うと実装できる
わざわざ記事にするほどではないjavaの型についてのお話
javaで型についてちょっとしたお話
javaでは型をプリミティブ型と参照型と大きく二分できる
プリミティブ型はオブジェクトではなく
逆に参照型はオブジェクトである
しかし、プリミティブ型をオブジェクトして扱いたい時がどうしてもある
そういう時はラッパークラスを使用してプリミティブ型をオブジェクトとして扱う
例えば
int x = 5
という変数をオブジェクトとして扱いたいときはint型のラッパークラスであるIntegerを使う
Integer translate = new Integer(x);
こうすることでプリミティブ型をラッパークラスを用いてオブジェクトに変換できる
そして値を取り出すには
int y = translate.intValue();
で、いい。
これはラッパークラスをプリミティブ型、今回であれば数値型に変換している
プリミティブ->オブジェクトの変換を
ボクシング変換という
その逆のオブジェクト、今回ならラッパークラス->プリミティブの変換を
アンボクシング変換という
java5以上ではわざわざ明示的に変換しなくても
これらは自動的に行われる、それらの変換を
オートボクシング、オートアンボクシングという
正しく実装するのが実は難しい二分探索
この前から珠玉のプログラミングっていう本を読んでて「なるほどな」っと思うことがあったのでまとめてみる
まず、二分探索って結構簡単に実装出来そうな雰囲気あるし
線形探索(先頭から一個ずつ探索する方法)よりも効率的な部分がある(大抵は線形探索でも十分間に合うし速度も問題ない)
それは線形探索がO(n/2)に対して二分探索はO(log n)で探索が可能だから
ただ要素数がある程度少ない場合は線形探索の方が良くて
むしろ無理に二分探索を実装するとバグの原因になったりするし
そもそもデータ構造と二分探索の探索法の関係でデータがソートされている必要がある
ここまでの前提を踏まえて
二分探索を考えてみると、すこしまずい書き方がある
例えばlow,highがあるとして
middleという二分探索で探索する集団の中央値をmiddle = (low+high)/2と定義してしまうことである
このように定義してしまうと6/2も7/2も結果として3になってしまう
特に要素がどう並んでいるか、どのような成分を持つかわからない時はこういう誤差を捨ててしまう処理はエラー、バグの原因になる
なので、high,low,middleと言った基準点を詳細な<,==,>といった比較で判別する必要がある
こういった細かいことを踏まえて
二分探索を正しく書くのは実は難しい
java8でソケットを使う
今回はjava8でソケットを使ってみる
ずっとCの話だったりして全くjavaの話は載せていなかったけど
自分が使う言語の中で一番使えるのはjavaっていう事実
ソケットで通信を扱うってどうやんの?
これがわかればあとは単純なコード書きだけ
ソケットを使う通信をプログラムするなら、サーバーとクライアントの2つのサイドを別々に作る必要がある
サーバー側では受動的ソケットという、接続待ちするソケットを作成し
クライアント側では能動的ソケットという、受動的ソケットに接続しにいくソケットを作成する
イメージは、受動的ソケットが凹で能動的ソケットが凸みたいな感じ
能動的ソケットが受動的ソケットに接続が完了した段階で通信が行われる
この時に、通信を行うのは受動的ソケットと受け入れソケットっていう受動的能動的ソケットが接続した際に生成する受動的ソケット側の新しいソケット
そんなこんなを実装して
文字列を一度だけやりとりするだけの簡単なプログラムを書いた
今回作成したプログラムを更に改良していくとチャットソフトのようなものができる
C言語の基礎を一日で復習してみた[メモ]
最近、パケットキャプチャをC言語作っていて
なんだかちょいちょい怪しい部分があるので、基礎を一日で復習してみようと思う
変数、ループ、場合分けなんかは大丈夫だと思っていたけど
Cでメモリを意識する際に重要なことを見つけた
まず、変数とは
値を格納するためのメモリ領域を確保し、名前をつけたもの
ということ(らしい)
次に強制的型変換について
強制的な型変換、つまり 「キャスト」 について
キャストすることで、変数や数値といった値を強制的に別のデータ型に変換できる
例えば、整数型の値を計算して浮動小数型に代入すると値に応じて小数部が切り捨てられたりするが
計算部を浮動小数型でキャストすることで欠損なく本来の値を代入できる
例をあげるなら
int a = 3;
int b = 2;
があったとき
float f;にf = a/b;と言った代入をしたならfは1.0になる
しかしf = (float)a/b;とすることで強制的にfloatに変換され
f = 1.5となる
次にキーボード入力
scanf("%dなど",変数のアドレス);で行う
また複数入力が可能であったり、区切り文字なんかも入れることができる
ただ入力形式が間違っていたりするとバグの原因になる
それを回避するためには入力を文字列として受け取り、文字列解析する方法が一般的
ここから先はしばらく出来る(ループ、if,switch)
ただ気をつけるならswitch~case文でcaseには数値しか入らず、条件式などが入らないこと
関数についても普段から使っているから大丈夫なはず
ただmain関数より下に記述するときはプロトタイプ宣言が必要
また関数は複数の戻り値を返すことができない(当然)
ここからだんだん内容が重くなる…
複数のデータをまとめて扱いたいときはもちろん「配列」を使う
配列は
データ型 配列名[要素数];で宣言でき
データ型 配列名 = {};と{}の中にデータをおいておくと初期化出来る
データを取り出すなら、配列名[インデックス];で出来る
配列のコピーはfor文でひたすらコピーすることもできるけど
string.hをインクルードしてmemcpy関数を使うのといい
memcpy(コピー先,コピー元,サイズ);
サイズはsizeof関数で出せるからこっちの方が簡単
配列を使うことで文字列を扱うことが出来る
文字型のデータ型があるので、これを配列にすることで実質的に文字列として表現できる
文字を配列で扱うと最後に"\0"が自動で追加されている(配列のサイズに余裕があるときだけ)
またこれは明示的に宣言可
この終端文字が無いと延々と文字列を表示してしまうためエラー、バグの原因となる
文字列はatoi関数で数値に変換できる
atoi関数stdlib.hにあるからそれをインクルードすればいい
文字列をコピーしたいときは
string.hをインクルードしstrcpy(コピー先、コピー元);
文字列を結合したいときは
string.hをインクルードして、strcat(コピー先,コピー元);
文字列合成したいときは
sprintf(生成先,初期式,変数…);で行える
文字列の長さはstring.hをインクルードしてstrlen関数を使う
文字列の比較は==で行えないのでstring.hをインクルードして
strcmp関数を使う、中身が同じである場合0が返ってくる
鬼のポインタ
多分ココが一番長くなる
ポインタとはなにか…
その前にまず、最初の方で書いたけど変数はメモリ上にあるって言うことが結構重要
そして変数のメモリ上での場所(入門書じゃ住所って言われたりする)をアドレスっていって
表示するには
&変数名をprintfとかで"%p"で呼び出せば出来る
ざっくり言えば&演算子はアドレスを求めることができる奴ということ
そしてなぜアドレスを知る必要があるかというと
関数は通常、引数がポインタなどメモリに関連したもので無い限り値渡しであり
実際に渡される変数の値はコピーされた関数内でどう変更されようと影響がない
しかし、アドレスをしりそこに代入することで呼び出し元の変数を書き換えることができる
これを使ったのがscanf関数
そして、このアドレスを格納する変数をポインタ型変数といい、ポインタ型変数に格納されている値をポインタ値という
ポインタは
データ型* 変数名;
データ型 *変数名;として宣言し、アドレスを代入しなければならない
(宣言しっぱなしはダメ)
ちなみにアドレスを代入するときは
変数名1 = &変数名2;でいい
ポインタに値を代入するときは
*変数名 = 値;でいい
また値の表示は
整数なら%dで*変数名でいい
さらにポインタを引数とする関数に大してアドレスをかっこ内に直接記述し渡すことができる
しかし、引数を配列にすると奇妙な動作する
例えば、配列の要素数が無視されたりコピー側を変更すると呼び出し元まで変更されるとか
実態は、配列を引数で渡すと実はアドレスが渡されているから
そのため配列を引数にしなくてもポインタが引数であれば配列は渡せる
しかもポインタ型を宣言しておきながらそれをいきなり配列チックに扱える
それはCの配列がアドレスを足し算することで表現していて、たまたまポインタと設計が似ていたから
だけど、配列は先頭の固定されたアドレスで、ポインタは可変なアドレスだから実は違う
さっきの使い方すると、ポインタに配列を渡した際にアクセスするとき
*(ポインタ変数+要素番号)みたいにアクセスできるこれがポインタ演算
ポインタについてはこれぐらいで
あと、構造体、共用体、マクロ、動的配列なんかあるけどそれは問題ないからメモらない
さくっとまとめたから色々間違ってるかもしれない