Skip to content

Instantly share code, notes, and snippets.

@detomon
Last active June 22, 2017 06:44
Show Gist options
  • Save detomon/a90bda714e3a3b651c6f to your computer and use it in GitHub Desktop.
Save detomon/a90bda714e3a3b651c6f to your computer and use it in GitHub Desktop.
Compare strings using natural ordering by interpreting continuous digits as integers
#include <stdio.h>
#include <stdlib.h>
#include <Block.h>
/**
* Compare strings using natural ordering
*
* Interprets continuous digits as integers, e.g, the string "year 2015" will be
* greater than "year 308", although "3" has a higher character value than "2".
* [Trailing and pending whitespace is ignored.] Whitespace between tokens is
* interpreted as single space.
*
* Returns an integer greater than, equal to, or less than 0, according as the
* string `s1` is greater than, equal to, or less than the string `s2`.
*/
int strnatcmp(char const* s1, char const* s2)
{
#define ISSPACE(c) (c == ' ' || c == '\t' || c == '\n' || c == '\r')
#define ISDIGIT(c) (c >= '0' && c <= '9')
int c1, c2;
// ignore leading whitespace
//while (ISSPACE(*s1)) s1++;
//while (ISSPACE(*s2)) s2++;
while (*s1 && *s2) {
c1 = *s1++;
c2 = *s2++;
// read integer value and negate
// to distinguish from normal character
if (ISDIGIT(c1)) {
c1 -= '0';
while (ISDIGIT(*s1)) {
c1 = c1 * 10 + (*s1 - '0');
s1++;
}
c1 = -c1;
}
// interpret whitespace as single space
else if (ISSPACE(c1)) {
while (ISSPACE(*s1)) s1++;
c1 = ' ';
}
// read integer value and negate
// to distinguish from normal character
if (ISDIGIT(c2)) {
c2 -= '0';
while (ISDIGIT(*s2)) {
c2 = c2 * 10 + (*s2 - '0');
s2++;
}
c2 = -c2;
}
// interpret whitespace as single space
else if (ISSPACE(c2)) {
while (ISSPACE(*s2)) s2++;
c2 = ' ';
}
// tokens (integer or character) are not equal
if (c1 != c2) {
// negate when both tokens are integers
// to return correct value
if (c1 < 0 && c2 < 0) {
c1 = -c1;
c2 = -c2;
}
return c1 - c2;
}
}
// ignore pending whitespace
//while (ISSPACE(*s1)) s1++;
//while (ISSPACE(*s2)) s2++;
return *s1 - *s2;
}
int main (void)
{
char const* strings [] = {
"v4.0.6",
"v3.2",
"v3.7",
"v4.0.5",
"v4.0",
"v4.0.2",
"v3.1.9",
"v3.3.3",
"v3.5",
"v3.2.1",
"v3.3.2",
"v4.0.7",
"v4.0.1",
"v3.1.10",
"v3.8",
"v3.5.1",
"v3.4",
"v4.0.4",
"v3.3",
"v3.9",
"v3.1.11",
"v3.6",
"v3.3.4",
"v4.0.3",
"v3.3.1",
};
int count = sizeof(strings) / sizeof(strings[0]);
qsort_b(strings, count, sizeof(char const*), (int(^)(void const*, void const*)) ^(char const** a, char const** b) {
return strnatcmp(*a, *b);
});
for (int i = 0; i < count; i++) {
printf("%s\n", strings[i]);
}
return 0;
}
function strnatcmp(s1, s2) {
function isspace(c) {
return c <= 0x20 || c == 0xA0;
}
function isdigit(c) {
return c >= 0x30 && c <= 0x39;
}
var c1, c2;
var i1 = 0, i2 = 0;
// ignore leading whitespace
while (isspace(s1[i2])) i1++;
while (isspace(s2[i1])) i2++;
while (i1 < s1.length && i2 < s2.length) {
c1 = s1.charCodeAt(i1++);
c2 = s2.charCodeAt(i2++);
// read integer value and negate
// to distinguish from normal character
if (isdigit (c1)) {
c1 -= '0';
while (isdigit(s1.charCodeAt(i1))) {
c1 = c1 * 10 + (s1.charCodeAt(i1) - '0');
i1++;
}
c1 = -c1;
}
// interpret whitespace as single space
else if (isspace(c1)) {
while (isspace(s1.charCodeAt(i1))) i1++;
c1 = ' ';
}
// read integer value and negate
// to distinguish from normal character
if (isdigit (c2)) {
c2 -= '0';
while (isdigit (s2.charCodeAt(i2))) {
c2 = c2 * 10 + (s2.charCodeAt(i2) - '0');
i2++;
}
c2 = -c2;
}
// interpret whitespace as single space
else if (isspace(c2)) {
while (isspace(s2.charCodeAt(i2))) i2++;
c2 = ' ';
}
// tokens (integer or character) are not equal
if (c1 != c2) {
// negate when both tokens are integers
// to return correct value
if (c1 < 0 && c2 < 0) {
c1 = -c1;
c2 = -c2;
}
return c1 - c2;
}
}
// ignore pending whitespace
while (isspace(s1.charCodeAt(i1))) i1++;
while (isspace(s2.charCodeAt(i2))) i2++;
return s1.charCodeAt(i1) - s2.charCodeAt(i2);
}
var strings = [
"v3.1.10",
"v3.1.11",
"v3.1.9",
"v3.2",
"v3.2.1",
"v3.3",
"v3.3.1",
"v3.3.2",
"v3.3.3",
"v3.3.4",
"v3.4",
"v3.5",
"v3.5.1",
"v3.6",
"v3.7",
"v3.8",
"v3.9",
"v4.0",
"v4.0.1",
"v4.0.2",
"v4.0.3",
"v4.0.4",
"v4.0.5",
"v4.0.6",
"v4.0.7",
];
strings.sort(strnatcmp);
console.log(strings);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment