Converting to and from human-readable byte counts
Converting some number of bytes (say 1024) into a human-readable byte count (say "1K") seems to be a common problem (simply search or ask StackOverflow). I also needed to go the other direction and turn things like "1.5 MB" into 1,572,864. Here's what I cooked up in C99:
// Adapted from http://stackoverflow.com/questions/3758606/ // how-to-convert-byte-size-into-human-readable-format-in-java void to_human_readable_byte_count(long bytes, int si, double *coeff, const char **units) { // Static lookup table of byte-based SI units static const char *suffix[][2] = { { "B", "B" }, { "kB", "KiB" }, { "MB", "MiB" }, { "GB", "GiB" }, { "TB", "TiB" }, { "EB", "EiB" }, { "ZB", "ZiB" }, { "YB", "YiB" } }; int unit = si ? 1000 : 1024; int exp = 0; if (bytes > 0) { exp = min( (int) (log(bytes) / log(unit)), (int) sizeof(suffix) / sizeof(suffix[0]) - 1); } *coeff = bytes / pow(unit, exp); *units = suffix[exp][!!si]; } // Convert strings like the following into byte counts // 5MB, 5 MB, 5M, 3.7GB, 123b, 456kB // with some amount of forgiveness baked into the parsing. long from_human_readable_byte_count(const char *str) { // Parse leading numeric factor char *endptr; errno = 0; const double coeff = strtod(str, &endptr); if (errno) return -1; // Skip any intermediate white space while (isspace(*endptr)) ++endptr; // Read off first character which should be an SI prefix int exp = 0; int unit = 1024; switch (toupper(*endptr)) { case 'B': exp = 0; break; case 'K': exp = 3; break; case 'M': exp = 6; break; case 'G': exp = 9; break; case 'T': exp = 12; break; case 'E': exp = 15; break; case 'Z': exp = 18; break; case 'Y': exp = 21; break; case ' ': case '\t': case '\0': exp = 0; goto done; default: return -1; } ++endptr; // If an 'i' or 'I' is present use SI factor-of-1000 units if (toupper(*endptr) == 'I') { ++endptr; unit = 1000; } // Next character must be one of B/empty/whitespace switch (toupper(*endptr)) { case 'B': case ' ': case '\t': ++endptr; break; case '\0': goto done; default: return -1; } // Skip any remaining white space while (isspace(*endptr)) ++endptr; // Parse error on anything but a null terminator if (*endptr) return -1; done: return exp ? coeff * pow(unit, exp / 3) : coeff; }
No comments:
Post a Comment