🦑

Arduino の String クラスの使い方

2023/04/28に公開

Ruby脳のための早見表

Arduino Ruby
s.length() s.length
s.toInt() s.to_i
s.toFloat() s.to_f
s.toDouble() s.to_d [1]
a + b a + b
a == b a == b
a.equals(b) a == b
! s += x s.concat(x)
! s.concat(x) s.concat(x)
s[i] s[i].ord
! s[i] = 'x' s[i] = "x"
s.charAt(i) s[i].ord
s.startsWith(x) s.start_with?(x)
s.endsWith(x) s.end_with?(x)
s.indexOf(x) s.index(x)
s.lastIndexOf(x) s.rindex(x)
s.substring(a, b) s[a..b]
a.compareTo(b) a <=> b
! s.remove(i, count) s[i, count] = ""
! s.replace("a", "xxx") s.gsub(/a/, "xxx")
! s.setCharAt(i, 'x') s[i] = "x"
! s.toLowerCase() s.downcase!
! s.toUpperCase() s.upcase!
! s.trim() s.strip!
a.equalsIgnoreCase(b) a.casecmp?(b)
s.reserve(n) new(capacity: n)
s.c_str() [s].pack("p")
s.getBytes(x, size) x.replace(s[...size.pred])

特徴と注意

  • 直感的に書けて便利
  • ただし to で始まるメソッドが破壊系だったりする

コンストラクタ

String("foo")     // => "foo"
String('a')       // => "a"
String(10)        // => "10"
String(10, DEC)   // => "10"
String(10, HEX)   // => "a"
String(10, BIN)   // => "1010"
String(0.555, 2)  // => "0.56"
String(true)      // => "1"
String(false)     // => "0"
String(NULL)      // => "0"

ここで 10 を a にしたりする必要ある? って思うのだけど Arduino のライブラリはこのような謎の仕様が多い。

安全なメソッドたち

length

String("foo").length();  // => 3

toInt, toFloat, toDouble

String(" -123.5555 ").toInt();     // => -1234
String(" -123.5555 ").toFloat();   // => -1234.56
String(" -123.5555 ").toDouble();  // => -1234.56
  • 内部で trim している
  • 小数点以下第3位を勝手に四捨五入している

比較

String("a") == "b"  // => false
String("a") != "b"  // => true
String("a") < "b"   // => true
String("a") > "b"   // => false
String("a") <= "b"  // => true
String("a") >= "b"  // => false
String("2") == 2    // => false
String("2") == "2"  // => true
  • 右辺は char な文字列か String のインスタンスであること
  • 右辺が文字列系以外だった場合にエラーにはならない

charAt, []

String("01")[-1]        // => 0
String("01")[0]         // => 48
String("01")[1]         // => 49
String("01")[2]         // => 0
String("01")[3]         // => 0

String("01").charAt(0)  // => 48
  • 領域外は 0

+

String("a") + "b"  // => "ab"
String("a") + 1    // => "a1"
  • 右辺は自動的に String でラップしたものとして扱ってくれる
  • 数字も文字列化される
  • とはいえ String("1") == 1 は真にならない (それでいい)

compareTo

String("s1").compareTo("s3")  // => -2
  • 左辺の文字コード - 右辺の文字コード がそのまま返ってくるので -2 になっている
  • その点で Ruby の <=> とは少し違う

c_str

String a = "01";
char *b = a.c_str();
b[0] = '_';
a  // => "_1"

同じ参照なので意図しない破壊に注意する。

startsWith, endsWith

String("abcd").startsWith("ab");  // => true
String("abcd").endsWith("cd");    // => true

equals, equalsIgnoreCase

String("foo").equals("foo");            // => true
String("foo").equalsIgnoreCase("FOO");  // => true

indexOf, lastIndexOf

String("abab").indexOf("ab");         // => 0
String("abab").indexOf("ab", 3);      // => -1
String("abab").lastIndexOf("ab");     // => 2
String("abab").lastIndexOf("ab", 0);  // => 0

substring

String("012345").substring(2, 5);  // => "234"

getBytes, toCharArray

char buf[4];
memset(buf, 'x', sizeof(buf));
String("abcd").getBytes(buf, 2);
buf[0]  // => 97
buf[1]  // => 0
buf[2]  // => 120
buf[3]  // => 120
char buf[4];
memset(buf, 'x', sizeof(buf));
String("abcd").toCharArray(buf, 2);
buf[0]  // => 97
buf[1]  // => 0
buf[2]  // => 120
buf[3]  // => 120
  • 違いがわからない
  • コピー先には終端の \0 も含まれる
  • そのため実際にコピーされるのは 指定の長さ - 1 になる

破壊系メソッド

concat, +=

String s = "a";
s += "b";
s  // => "ab"

String s = "a";
s.concat("b");
s  // => "ab"
  • += は concat のエイリアスでしかない
  • += ではなく << にしてほしかった

remove

String s = "01234567";
s.remove(3, 2);
s  // => "012567"

インデックス 3 から 2 文字削除する。

replace

String s = "abcabc";
s.replace("bc", "___");
s  // => "a___a___"

元と先のサイズを合わせる必要はない。

setCharAt, []=

String s = "0123";
s.setCharAt(1, '_');
s[2] = '_';
s[4] = '_';
s  // => "0__3"

範囲外には書き込めないためバッファオーバーフローの心配がない。

toLowerCase, toUpperCase

String s = "AbCd";
s.toLowerCase();
s  // => "abcd"

String s = "AbCd";
s.toUpperCase();
s  // => "ABCD"

to で始まるのは Immutable なメソッドという全言語共通の暗黙ルールが破壊された。

trim

String s = " foo ";
s.trim();
s  // => "foo"

よくわからないメソッド

reserve

String s = "";
s.reserve(2);
s += "12345";
s  // => "12345"

あらかじめ余裕を持って確保しておけば concat 時のバッファの拡張回数が減るってことかな?

参照

https://www.arduino.cc/reference/en/language/variables/data-types/string/

脚注
  1. require "bigdecimal/util" が必要 ↩︎

Discussion