|
|
1.1 root 1: //
2: // nono
1.1.1.3 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.6 root 7: //
8: // 文字列操作
9: //
10:
1.1 root 11: #include "mystring.h"
1.1.1.2 root 12: #include <algorithm>
1.1 root 13:
14: std::string
15: string_format(const char *fmt, ...)
16: {
17: va_list ap;
18: char *buf;
19:
20: va_start(ap, fmt);
21: vasprintf(&buf, fmt, ap);
22: va_end(ap);
23: std::string rv(buf);
24: free(buf);
25:
26: return rv;
27: }
28:
1.1.1.2 root 29: // 文字列 str から先頭の連続する空白文字を取り除いた新しい文字列を返す。
30: std::string
31: string_ltrim(const std::string& str)
32: {
33: auto it = str.begin();
34: for (; it != str.end(); it++) {
35: if (!isspace(*it))
36: break;
37: }
38: return std::string(it, str.end());
39: }
40:
41: // 文字列 str から末尾の連続する空白文字を取り除く (str を書き換える)。
42: void
43: string_rtrim(std::string& str)
44: {
45: while (isspace(*str.rbegin())) {
46: str.pop_back();
47: }
48: }
49:
50: // 文字列 str から末尾の連続する空白文字を取り除く (str を書き換える)。
51: void
52: rtrim(char *str)
53: {
54: char *p = strchr(str, '\0');
1.1.1.5 root 55: while (--p >= str && isspace((int)*p)) {
1.1.1.2 root 56: *p = '\0';
57: }
58: }
59:
1.1.1.5 root 60: // 文字列 str から先頭と末尾の連続する空白文字を取り除いた新しい文字列を返す。
61: std::string
62: string_trim(const std::string& str)
63: {
64: int s = 0;
65: int e = str.size();
66:
67: for (; s < e; s++) {
68: if (!isspace(str[s]))
69: break;
70: }
71: for (e--; e >= s; e--) {
72: if (!isspace(str[e]))
73: break;
74: }
75:
76: return str.substr(s, e - s + 1);
77: }
78:
1.1.1.2 root 79: // 文字列 src 中の ASCII 大文字を小文字にした新しい文字列を返す。
80: std::string
81: string_tolower(const std::string& src)
82: {
83: std::string dst(src);
84: std::transform(dst.begin(), dst.end(), dst.begin(),
85: [](unsigned char c){ return std::tolower(c); });
86: return dst;
87: }
88:
1.1.1.7 root 89: // 文字列 src 中の ASCII 小文字を大文字にした新しい文字列を返す。
90: std::string
91: string_toupper(const std::string& src)
92: {
93: std::string dst(src);
94: std::transform(dst.begin(), dst.end(), dst.begin(),
95: [](unsigned char c){ return std::toupper(c); });
96: return dst;
97: }
98:
1.1.1.4 root 99: // 文字列 lhs の先頭が rhs と大文字小文字の区別なしで一致すれば true を返す。
100: // ASCII 専用。
101: // C++20 の starts_with に似せておく。
102: bool
103: starts_with_ignorecase(const std::string& lhs, const std::string& rhs)
104: {
105: if (lhs.length() < rhs.length()) {
106: return false;
107: }
108: #if 0
109: return std::equal(
110: lhs.begin(), lhs.begin() + rhs.length(),
111: rhs.begin(),
112: [](std::string::value_type l, std::string::value_type r) {
113: return std::tolower(l) == std::tolower(r);
114: }
115: );
116: #else
117: // こっちのほうが分かりやすいよな
118: return strncasecmp(lhs.c_str(), rhs.c_str(), rhs.length()) == 0;
119: #endif
120: }
121:
122: // 文字列 str (長さ len) を文字 c で分割したリストを返す。
123: std::vector<std::string>
1.1.1.5 root 124: string_split(const char *str, int len, char c, int nlimit)
1.1.1.4 root 125: {
126: std::vector<std::string> list;
127:
1.1.1.5 root 128: // 空文字列なら空リスト
129: if (len == 0) {
130: return list;
131: }
132:
133: int pos = 0;
134: int end = 0;
135:
136: for (; ; pos = end + 1) {
137: // 上限に達するならこれ以降は一要素として返す
138: if (nlimit > 0 && list.size() >= nlimit - 1) {
139: list.emplace_back(str + pos, len - pos);
140: break;
141: }
142:
1.1.1.4 root 143: const char *p = strchr(str + pos, c);
144: if (p) {
145: end = p - str;
146: } else {
147: end = len;
148: }
149:
150: list.emplace_back(str + pos, end - pos);
1.1.1.5 root 151: if (p == NULL)
152: break;
1.1.1.4 root 153: }
154:
155: return list;
156: }
157:
1.1.1.6 root 158: // val を3桁ずつカンマ区切りした文字列にして返す。最大は 26桁。
159: // ex) 123 -> "138"
160: // 12345 -> "12,345"
161: std::string
162: format_number(uint64 val)
163: {
164: // 1 2 3 4 5 6
165: // UINT64_MAX = 18,446,744,073,709,551,615
166: char part[6][8];
167: char buf[32];
168: int n;
169:
170: n = 0;
171: memset(&part, 0, sizeof(part));
172: while (val >= 1000) {
173: uint32 r = val % 1000;
174: val /= 1000;
175:
176: snprintf(part[n], sizeof(part[n]), ",%03u", r);
177: n++;
178: }
179: // この時点で
180: // part[0] = ",615";
181: // part[1] = ",551";
182: // :
183: // part[5] = ",446";
184:
185: // 先頭(val は 1000未満)
186: snprintf(buf, sizeof(buf), "%u", (uint32)val);
187:
188: // part を連結
189: while (--n >= 0) {
190: strlcat(buf, part[n], sizeof(buf));
191: }
192: return std::string(buf);
193: }
194:
1.1.1.7 root 195: // value を width 桁の16進数文字列にして返す。"%0{width}x" みたいな感じ。
196: // strhex(0x12345678, 4) -> "5678"
197: // strhex(0x00000001, 3) -> "001"
198: std::string
199: strhex(uint32 value, int width)
200: {
201: std::string s;
202:
203: for (width -= 1; width >= 0; width--) {
204: uint32 d = (value >> (width * 4)) & 0x0f;
205: if (__predict_true(d < 10)) {
206: s += '0' + d;
207: } else {
208: s += 'a' + d - 10;
209: }
210: }
211: return s;
212: }
213:
1.1.1.8 ! root 214: #define FORMAT_FULL 0
! 215: #define FORMAT_SEC 1
! 216:
! 217: // 経過時間 t を文字列にして返す。
! 218: // 文字列長は、t の大きさ(とフォーマット指定)によって以下の通り。
! 219: //
! 220: // 0 1 2
! 221: // 01234567890123456789012345
! 222: // "0.mmm'uuu'nnn" 10秒未満、または FORMAT_SEC なら13桁
! 223: // "59.mmm'uuu'nnn" 1分未満なら14桁
! 224: // " 9:59.mmm'uuu'nnn" 1時間未満なら17桁
! 225: // " 9:59:59.mmm'uuu'nnn" 24時間未満なら20桁
! 226: // "999d 23:59:59.mmm'uuu'nnn" 1日以上なら25桁。これが最大幅。
! 227: //
! 228: // FORMAT_SEC は1桁秒以下の場合 (実際には1秒未満の場合) に用いる。
! 229: // FORMAT_FULL は 1000日経過すると桁がずれるけど、それはもういいだろう。
! 230: //
! 231: // 10秒未満の場合だけ %2u ではなく %u で1桁切り詰めているが、これは
! 232: // FORMAT_SEC との互換性のため。その必要のない10分未満と10時間未満は
! 233: // どちらも %2u で表記し桁数を維持することに努める。
! 234: static const std::string
! 235: TimeToStrF(uint64 t, int format)
! 236: {
! 237: char buf[32];
! 238: char *p;
! 239: size_t len;
! 240: int n;
! 241:
! 242: uint ns = t % 1000;
! 243: t /= 1000;
! 244: uint us = t % 1000;
! 245: t /= 1000;
! 246: uint ms = t % 1000;
! 247: t /= 1000;
! 248:
! 249: uint s, m, h, d;
! 250: if (format == FORMAT_FULL) {
! 251: s = t % 60;
! 252: t /= 60;
! 253: m = t % 60;
! 254: t /= 60;
! 255: h = t % 24;
! 256: t /= 24;
! 257: d = t;
! 258: } else {
! 259: s = t;
! 260: m = 0;
! 261: h = 0;
! 262: d = 0;
! 263: }
! 264:
! 265: p = buf;
! 266: len = sizeof(buf);
! 267: if (d) {
! 268: n = snprintf(p, len, "%3ud %02u:%02u:%02u", d, h, m, s);
! 269: p += n;
! 270: len -= n;
! 271: } else if (h) {
! 272: n = snprintf(p, len, "%2u:%02u:%02u", h, m, s);
! 273: p += n;
! 274: len -= n;
! 275: } else if (m) {
! 276: n = snprintf(p, len, "%2u:%02u", m, s);
! 277: p += n;
! 278: len -= n;
! 279: } else {
! 280: n = snprintf(p, len, "%u", s);
! 281: p += n;
! 282: len -= n;
! 283: }
! 284: n = snprintf(p, len, ".%03u'%03u'%03u", ms, us, ns);
! 285: p += n;
! 286: len -= n;
! 287:
! 288: return std::string(buf, p - buf);
! 289: }
! 290:
! 291: const std::string
! 292: TimeToStr(uint64 t)
! 293: {
! 294: return TimeToStrF(t, FORMAT_FULL);
! 295: }
! 296:
! 297: const std::string
! 298: SecToStr(uint64 t)
! 299: {
! 300: return TimeToStrF(t, FORMAT_SEC);
! 301: }
! 302:
1.1.1.7 root 303:
1.1.1.6 root 304: #if defined(SELFTEST)
1.1 root 305: #include <cstdio>
1.1.1.6 root 306: #include "stopwatch.h"
307: std::string s;
1.1 root 308: int main()
309: {
1.1.1.6 root 310: Stopwatch sw;
311: sw.Start();
312: for (uint64 i = 0; i < 10000000; i += 3) {
313: s = format_number(i);
314: if (s.empty())
315: return 0;
316: }
317: sw.Stop();
318: uint64 t = sw.Elapsed();
319: printf("%ld.%03ld msec\n", t / 1000 / 1000, (t / 1000) % 1000);
1.1 root 320: return 0;
321: }
322: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.