|
|
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.8 root 215: #define FORMAT_FULL 0
216: #define FORMAT_SEC 1
217:
218: // 経過時間 t を文字列にして返す。
219: // 文字列長は、t の大きさ(とフォーマット指定)によって以下の通り。
220: //
221: // 0 1 2
222: // 01234567890123456789012345
223: // "0.mmm'uuu'nnn" 10秒未満、または FORMAT_SEC なら13桁
224: // "59.mmm'uuu'nnn" 1分未満なら14桁
225: // " 9:59.mmm'uuu'nnn" 1時間未満なら17桁
226: // " 9:59:59.mmm'uuu'nnn" 24時間未満なら20桁
227: // "999d 23:59:59.mmm'uuu'nnn" 1日以上なら25桁。これが最大幅。
228: //
229: // FORMAT_SEC は1桁秒以下の場合 (実際には1秒未満の場合) に用いる。
230: // FORMAT_FULL は 1000日経過すると桁がずれるけど、それはもういいだろう。
231: //
232: // 10秒未満の場合だけ %2u ではなく %u で1桁切り詰めているが、これは
233: // FORMAT_SEC との互換性のため。その必要のない10分未満と10時間未満は
234: // どちらも %2u で表記し桁数を維持することに努める。
235: static const std::string
236: TimeToStrF(uint64 t, int format)
237: {
238: char buf[32];
239: char *p;
240: size_t len;
241: int n;
242:
243: uint ns = t % 1000;
244: t /= 1000;
245: uint us = t % 1000;
246: t /= 1000;
247: uint ms = t % 1000;
248: t /= 1000;
249:
250: uint s, m, h, d;
251: if (format == FORMAT_FULL) {
252: s = t % 60;
253: t /= 60;
254: m = t % 60;
255: t /= 60;
256: h = t % 24;
257: t /= 24;
258: d = t;
259: } else {
260: s = t;
261: m = 0;
262: h = 0;
263: d = 0;
264: }
265:
266: p = buf;
267: len = sizeof(buf);
268: if (d) {
269: n = snprintf(p, len, "%3ud %02u:%02u:%02u", d, h, m, s);
270: p += n;
271: len -= n;
272: } else if (h) {
273: n = snprintf(p, len, "%2u:%02u:%02u", h, m, s);
274: p += n;
275: len -= n;
276: } else if (m) {
277: n = snprintf(p, len, "%2u:%02u", m, s);
278: p += n;
279: len -= n;
280: } else {
281: n = snprintf(p, len, "%u", s);
282: p += n;
283: len -= n;
284: }
285: n = snprintf(p, len, ".%03u'%03u'%03u", ms, us, ns);
286: p += n;
287: len -= n;
288:
289: return std::string(buf, p - buf);
290: }
291:
292: const std::string
293: TimeToStr(uint64 t)
294: {
295: return TimeToStrF(t, FORMAT_FULL);
296: }
297:
298: const std::string
299: SecToStr(uint64 t)
300: {
301: return TimeToStrF(t, FORMAT_SEC);
302: }
303:
1.1.1.7 root 304:
1.1.1.6 root 305: #if defined(SELFTEST)
1.1 root 306: #include <cstdio>
1.1.1.6 root 307: #include "stopwatch.h"
308: std::string s;
1.1 root 309: int main()
310: {
1.1.1.6 root 311: Stopwatch sw;
312: sw.Start();
313: for (uint64 i = 0; i < 10000000; i += 3) {
314: s = format_number(i);
315: if (s.empty())
316: return 0;
317: }
318: sw.Stop();
319: uint64 t = sw.Elapsed();
320: printf("%ld.%03ld msec\n", t / 1000 / 1000, (t / 1000) % 1000);
1.1 root 321: return 0;
322: }
323: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.