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