文字列クラス - MIKO.ORG

1
第6章
文字列クラス
6.1 QByteArray
6.1.1 概要
QByteArray はバイトの配列を扱うためのクラスです。古典的な C 言語のプログラムでは、生のバイトの並びやナ
ル終止文字列を扱うのに const char *を使いますが、Qt では QByteArray によってそれらを扱うことができます。
QByteArray には便利な関数が多数用意されており、const char * を直接利用するよりもはるかに簡単にバイト配
列を操作することができます。必要に応じてバイト配列のサイズは動的に増減しますし、バイト配列の末尾には常にナ
ル文字がターミネータとして自動的に付加されます。
また、QByteArray では暗黙のデータ共有 (copy-on-write) がサポートされており、メモリの利用量削減と不必要な
データコピーの回避が図られています。たとえば、関数を呼び出すのに QByteArray のデータを call-by-value で渡し
たとしても、暗黙のデータ共有のおかげで不必要なデータ全体のコピーは発生しません。
文字列を扱うためのクラスとしては、他に QString が用意されています。一般的な用途には QString を選択したほ
うが都合がよいでしょう。QString クラスでは、文字は 16 ビットの Unicode として表現されますので、非-ASCII/
非-Latin-1 の文字も簡単に扱うことができます。また、Qt の API では、至る所で文字列の受け渡しに QString が使
われていますので、それらインターフェースとの親和性の観点からも QString の使用が望ましいといえます。
QString よりも QByteArray の利用がふさわしい用途としては、生のバイナリデータを格納する必要がある場合と、
メモリの節約が大変重要である場合 (たとえば Qt/Embedded で利用する場合) が挙げられます。
6.1.2 主要なインターフェース
バイト列の生成
QByteArray クラスのコンストラクタを呼び出すことで、メモリ上にバイト配列を用意することができます。
デフォルトコンストラクタが呼び出された場合、および、コンストラクタの引数として (const char *)0 が渡され
た場合には、空のバイト配列が生成されます。生成されるバイト配列の内容を指定したい場合には、コンストラクタに
const char *を与えてください。なお、生成されたバイト配列の末尾にはナル文字が自動的に付きます。
バイト配列が空かどうかを調べるには isEmpty() を使います。また、size() によってバイト配列の大きさを調べ
ることができます。バイト配列の末尾に自動的に付与されるナル文字は、 size() の結果に勘定されません。バイト配
列の中にナル文字が含まれていた場合、size() はそれを一文字として数えます。
QByteArray empty_array;
// empty_array.isEmpty() == true;
第 6 章 文字列クラス
2
// empty_array.size() == 0
QByteArray array("Hello");
// array.isEmpty() == false;
// array.size() == 5
引数として与えられたデータが const char *である場合、QByteArray はデータをディープコピーして利用しま
す。そのため、バイト配列を書き換えても副作用が発生するようなことはありません。効率上の理由から文字データの
ディープコピーをおこなわせたくない場合には代わりに fromRawData() を使うこともできますが、不用心にバイト配
列を書き換えたりしないように注意してください。
static const char cstr[] = "const string";
QByteArray array(QByteArray::fromRawData(cstr, sizeof(cstr)));
// array[2] = ’x’ などとして書き換えることはできない
バイト配列を生成した後で、そのサイズを変更するには resize() を使います。現状よりも大きなサイズが指定され
た場合、新しく追加されたバイト配列上にあるバイトの値は未定義となります。また、小さなサイズが指定された場合
には、そのサイズまでバイト配列は縮小されます。
QByteArray array;
array.resize(1024);
// array.isEmpty() == false;
// array.size() == 1024
append() および operator+=() を使えば、バイト配列の末尾にデータを追加することができます。また、バイト配
列の先頭にデータを追加するには prepend() を使います。
QByteArray array("morph");
array.prepend("poly");
array.append("ism");
// array == QByteArray("polymorphism")
指定した位置にバイト配列を挿入するには insert() を使います。バイト配列を挿入する位置が元のバイト配列の範
囲外であった場合には、resize() によって元のバイト配列が拡張されたのちに挿入処理がおこなわれます。
QByteArray array("hang");
array.insert(2, QByteArray("cki"));
// array == QByteArray("hacking")
バイト配列の削除
バイト配列の内容を全て削除し、空のバイト配列に戻すには clear() を使います。
QByteArray array("hello");
array.clear();
// array.isEmpty() == true
6.1 QByteArray
3
バイト配列のうちの一部を削除してバイト配列を切り詰めたい場合には、削除する範囲の開始位置と長さを指定して
remove() を呼び出します。たとえ削除する範囲がバイト配列の外にはみ出していたとしても、それによる悪影響はあ
りません。
QByteArray array("hacking");
array.remove(2, 3);
// array == QByteArray("hang")
バイト配列の末尾から指定したバイト分だけ削除するには chop() を使います。また、truncate() は先頭から指定
したバイト分だけを残し、バイト列を切り詰める働きをします。
QByteArray array("polymorphism");
array.chop(3);
// array == QByteArray("polymorph")
array.truncate(4);
// array == QByteArray("poly")
trimmed() は、バイト配列の両端からホワイトスペースを取り除いたものをコピーとして返します。ホワイトスペー
スの定義としては、標準 C++ の isspace() が真となる文字のことをいい、’\t’, ’\n’, ’\v’, ’\f’, ’\r’, ’ ’
が含まれます。また、simplified() は trimmed() の機能に加え、複数の連続したホワイトスペースをシングルス
ペース文字に置き換えたものをコピーして返します。
QByteArray src(" All works and no play
makes Jack a dull boy.
");
QByteArray dst1(src.trimmed());
QByteArray dst2(src.simplified());
// dst1 == QByteArray("All works and no play
makes Jack a dull boy.")
// dst2 == QByteArray("All works and no play makes Jack a dull boy.")
バイト配列からのデータ抽出
at() または operator[]() を使うことによって、バイト配列の中から指定したインデックスの位置にあるバイト
データをとり出すことができます。処理速度に関していえば、at() の動作は operator[]() よりも高速であるため、
読み出し専用のアクセスの場合には at() を使うのが良いでしょう。なお、at() を呼び出す際には、インデックスが
配列のサイズを越えてはいけません。
QByteArray array("hello");
// array.at(0) == ’h’
// array[1] == ’e’
指定した位置から始まる複数のバイトデータをコピーして一度に取り出すには left(), right(), mid() を使いま
す。取り出す範囲がバイト配列の大きさを越えていた場合には、バイト配列全体が返されます。
QByteArray array("hello");
// array.left(4) == QByteArray("hell");
// array.right(4) == QByteArray("ello");
第 6 章 文字列クラス
4
// array.mid(1, 3) == QByteArray("ell");
バイト配列自身へのポインタを得るには data() または constData() を呼び出してください。ポインタの指し示す
データはナル終止文字列となりますので、そのまま標準 C/C++ の関数に渡して使うこともできます。
QByteArray array("Hello, world.");
cout << array.constData() << endl;
split() は指定したデリミタでバイト配列を区切り、その結果を QList<QByteArray>に格納して返す関数です。
QByteArray array("alpha,beta,gamma");
QList<QByteArray> list(array.split(’,’));
foreach (QByteArray ba, list) {
cout << ba.constData() << endl;
}
バイト配列の編集
バイト配列が非 const のデータである場合、operator[]() はバイトの参照を返します。これを代入式の左辺として
使うことで、バイト単位で配列を書き換えることができます。
QByteArray array("write");
array[2] = ’o’;
// array == QByteArray("wrote")
複数のバイトを一度に書き換えたいなら、replace() を使うのが良いでしょう。書き換える位置と長さ、それに書
き込むバイト列の 3 つの引数で呼び出すことで、既存のバイト配列の一部を別のバイト配列の内容に書き換えることが
できます。機能的には remove() と insert() を同時におこなうものと考えることができます。
QByteArray array("hacking");
array.replace(0, 4, QByteArray("fix"));
// array == QByteArray("fixing")
また、replace() には 2 つの引数を持つオーバーロード関数としても定義されています。こちらの関数では、バイ
ト配列中に含まれる特定のバイト列を、一括して別のバイト列に置き換えることができます。
QByteArray array("hacking hacking");
array.replace(QByteArray("hack"), QByteArray("fix"));
// array == QByteArray("fixing fixing")
toLower() と toUpper() は、大小文字を変換したバイト列のコピーを返します。ただし、バイト配列中に ASCII
文字以外の文字が含まれていた場合には、無関係なデータまで巻き込んで書き換えてしまうおそれがありますので注意
してください。なお、文字を Unicode で格納する QStrings では、このような問題は発生しません。
QByteArray array("alpha beta");
array = array.toUpper();
// array == QString("ALPHA BETA");
6.1 QByteArray
5
バイト配列の検索と比較
QByteArray が特定の文字または文字列を含むかどうかを簡単にチェックしたいのなら、contains() を使ってくだ
さい。特定の文字または文字列がバイト配列の中で何回使われているのかを調べるには、count() を使ってください。
QByteArray array(" All works and no play
makes Jack a dull boy.
");
// array.contains("ll") == true
// array.count("ll") == 2
バイト配列中における特定の文字または短文の出現位置を調べたいなら、indexOf() または lastIndexOf() を呼
び出してください。前者は与えられたインデックスの位置よりも先にある文字列を検索し、後者はもとに近い側の文字
列を検索します。指定した文字または短文が見つかれば、そのインデックス位置を返します。見つからなければ -1 を
返します。
QByteArray ba("We must be <b>bold</b>, very <b>bold</b>");
int j = 0;
while ((j = ba.indexOf("<b>", j)) != -1) {
cout << "Found <b> tag at index position " << j++ << endl;
}
startsWith() と endsWith() によって、バイト配列が特定のバイト配列で始まるかどうか、また、終わるかどうか
を調べることができます。
QByteArray array("http://www.miko.org/");
if (array.startsWith(QByteArray("http://"))) {
...
}
バイト列同士を比較するには、operator<(), operator<=(), operator==(), operator>=(), などのオーバー
ロードされた演算子を使っておこなうことができます。文字の値に基づいて比較の処理をおこなうので非常に高速に動
作しますが、その結果は人間が期待するものとは異なることがあります。ユーザが目にする文字列をソートする場合に
は QString::localeAwareCompare() を使ったほうが良いでしょう。
歴史的な理由から、QByteArray はナルのバイト配列と空のバイト配列を区別します。ナル のバイト配列は
QByteArray をデフォルトコンストラクタで初期化するか、あるいはコンストラクタに (const char *)0 を渡して初
期化することで生成できます。対して、空 のバイト配列とはサイズが 0 である任意のバイト配列のことをいいます。
ナルのバイト配列は常に空のバイト配列でもありますが、空のバイト配列がナルのバイト配列であるとは限りません。
QByteArray().isNull();
// returns true
QByteArray().isEmpty();
// returns true
QByteArray("").isNull();
// returns false
QByteArray("").isEmpty();
// returns true
QByteArray("abc").isNull();
// returns false
QByteArray("abc").isEmpty();
// returns false
第 6 章 文字列クラス
6
isNull() を除く全ての関数は、ナルのバイト配列を空のバイト配列と同様に扱います。たとえば、ナルのバ
イト配列に対する data() は ’\0’ 文字へのポインタ (ナルポインタではない) を返しますし、QByteArray() と
QByteArray("") を比較した結果は等しくなります。不必要な混乱を避けるため、常に isEmpty() を使うようにし、
isNull() は使わないようにされることをお勧めします。
データコンバージョン
バイト配列の内容を整数に変換するには、toInt(), toUInt(), toShort(), toUShort(), toLongLong(),
toULongLong() を使います。引数には bool へのポインタと変換時の底の値を指定することができます。省略するこ
ともできます。変換に失敗した場合、bool には偽が返り、関数の戻り値は 0 となります。また、底は 0(自動認識) も
しくは 2∼36 の値でなければなりません。デフォルトでは 10 となります。
QByteArray array("ff");
bool ok;
int hex = array.toInt(&ok, 16); // ok == true, hex == 255
int dec = array.toInt(&ok, 10); // ok == false, dex == 0
バイト配列の内容を実数に変換するには、toDouble(), toFloat() を使います。引数には bool へのポインタを指
定することができます。省略することもできます。変換に失敗した場合、bool には偽が返り、関数の戻り値は 0.0 と
なります。
QByteArray array("3.1415");
bool ok;
double r = array.toDouble(&ok);
数値のデータをバイト配列に変換するには number() を使います。
int n = 255;
qDebug() << QByteArray::number(n, 16).constData(); // ff
6.2 QString
6.3 QStringList