さっと検索した範囲だとサンプルが見つからず、単純だけど自分で作ったのでメモ。例によって力技。数値変換せずに文字列比較だけ(ユニコード前提)。
データは、他の自サイトに最近1ヵ月ほどでアクセスのあったGooglebotやGoogleの他サービスのIPアドレス。(ちなみにいずれも全リストが公開されている。Googlebot / 他サービス使用IP。参照した記事はこちら )
比較関数
まず比較関数を用意する。とりあえず、よく使うstd::wstring版。(不正な文字列とかの対策は足りないかも)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
bool less_ipaddress( const std::wstring& lhs, const std::wstring& rhs ) { if( lhs == rhs ) return false; std::wstring lip = lhs; std::wstring rip = rhs; const std::wstring pr( L"." ); for( int i = 0; i < 3; ++i ) { // オクテットの長さを比較 const auto l_pos = lip.find( pr ); const auto r_pos = rip.find( pr ); if( l_pos != r_pos ) return l_pos < r_pos; // 区切りか見つからなければ(取得失敗)終了 if( l_pos == std::wstring::npos && r_pos == std::wstring::npos ) return false; // 長さが一緒なら中身を比較 const auto l_str = lip.substr( 0, l_pos ); const auto r_str = rip.substr( 0, r_pos ); if( l_str != r_str ) return l_str < r_str; // 中身が一緒なら削除して次へ lip = lip.substr( l_pos + 1 ); rip = rip.substr( r_pos + 1 ); } // 第3オクテットまで同じだった場合 if( lip.length() == rip.length() ) return lip < rip; return lip.length() < rip.length(); } |
(降順ソートは比較を逆にするだけ。)
テストコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#include <vector> #include <algorithm> #include <iostream> #include <conio.h> int _tmain(int argc, _TCHAR* argv[]) { std::vector<std::wstring> gbots = { {L"107.178.234.92"},{L"107.178.234.88"},{L"107.178.234.85"},{L"107.178.234.23"}, {L"107.178.234.158"},{L"107.178.234.156"},{L"107.178.234.155"},{L"107.178.234.147"}, {L"107.178.234.144"},{L"66.249.88.44"},{L"130.211.54.158"},{L"66.249.88.45"}, {L"193.186.4.134"},{L"193.186.4.152"},{L"193.186.4.153"},{L"66.249.88.46"}, {L"66.249.88.47"},{L"72.14.199.68"},{L"72.14.199.70"},{L"72.14.199.72"}, {L"72.14.199.73"},{L"72.14.199.76"},{L"72.14.199.77"},{L"72.14.199.79"}, {L"72.14.199.80"},{L"72.14.201.152"},{L"72.14.201.153"},{L"74.125.150.1"}, {L"74.125.150.19"},{L"74.125.150.20"},{L"74.125.150.23"},{L"74.125.150.24"}, {L"74.125.150.25"},{L"64.233.172.180"},{L"66.102.8.152"},{L"66.102.8.158"}, {L"66.249.64.152"},{L"66.249.66.86"},{L"74.125.150.27"},{L"74.125.150.28"}, {L"74.125.150.31"},{L"74.125.212.235"},{L"66.249.79.137"},{L"74.125.212.237"}, {L"74.125.212.239"},{L"74.125.212.241"},{L"74.125.212.243"},{L"74.125.214.16"}, {L"74.125.214.18"},{L"74.125.214.20"},{L"66.249.72.239"},{L"66.249.72.241"}, {L"66.249.72.243"},{L"74.125.214.26"},{L"66.249.79.133"},{L"66.249.79.135"}, {L"66.249.82.188"},{L"66.249.79.139"},{L"66.249.79.141"},{L"66.249.79.143"}, {L"66.249.79.145"},{L"66.249.79.147"},{L"66.249.79.153"},{L"66.249.82.16"}, {L"66.249.82.176"},{L"66.249.82.178"},{L"66.249.82.18"},{L"66.249.82.180"}, {L"66.249.82.186"},{L"66.249.82.190"},{L"66.249.82.20"},{L"66.249.82.26"}, {L"66.249.82.28"},{L"66.249.82.30"},{L"66.249.83.17"},{L"66.249.83.19"}, {L"66.249.83.21"},{L"66.249.88.40"},{L"66.249.88.42"},{L"66.249.68.21"}, {L"66.249.68.13"},{L"66.249.68.1"},{L"66.249.79.20"},{L"66.249.74.16"}, {L"66.249.68.9"},{L"66.249.68.6"},{L"66.249.68.5"},{L"66.249.68.30"}, {L"66.249.68.28"},{L"66.249.68.25"},{L"66.249.68.24"},{L"66.249.68.20"}, {L"66.249.68.17"} }; std::sort( gbots.begin(), gbots.end(), less_ipaddress ); for( const auto& s : gbots ) std::wcout << s << std::endl; getch(); return 0; } |
出力結果
64.233.172.180
66.102.8.152
66.102.8.158
66.249.64.152
66.249.66.86
66.249.68.1
66.249.68.5
66.249.68.6
66.249.68.9
66.249.68.13
66.249.68.17
66.249.68.20
66.249.68.21
66.249.68.24
66.249.68.25
66.249.68.28
66.249.68.30
66.249.72.239
66.249.72.241
66.249.72.243
66.249.74.16
66.249.79.20
66.249.79.133
66.249.79.135
66.249.79.137
66.249.79.139
66.249.79.141
66.249.79.143
66.249.79.145
66.249.79.147
66.249.79.153
66.249.82.16
66.249.82.18
66.249.82.20
66.249.82.26
66.249.82.28
66.249.82.30
66.249.82.176
66.249.82.178
66.249.82.180
66.249.82.186
66.249.82.188
66.249.82.190
66.249.83.17
66.249.83.19
66.249.83.21
66.249.88.40
66.249.88.42
66.249.88.44
66.249.88.45
66.249.88.46
66.249.88.47
72.14.199.68
72.14.199.70
72.14.199.72
72.14.199.73
72.14.199.76
72.14.199.77
72.14.199.79
72.14.199.80
72.14.201.152
72.14.201.153
74.125.150.1
74.125.150.19
74.125.150.20
74.125.150.23
74.125.150.24
74.125.150.25
74.125.150.27
74.125.150.28
74.125.150.31
74.125.212.235
74.125.212.237
74.125.212.239
74.125.212.241
74.125.212.243
74.125.214.16
74.125.214.18
74.125.214.20
74.125.214.26
107.178.234.23
107.178.234.85
107.178.234.88
107.178.234.92
107.178.234.144
107.178.234.147
107.178.234.155
107.178.234.156
107.178.234.158
130.211.54.158
193.186.4.134
193.186.4.152
193.186.4.153
66.102.8.152
66.102.8.158
66.249.64.152
66.249.66.86
66.249.68.1
66.249.68.5
66.249.68.6
66.249.68.9
66.249.68.13
66.249.68.17
66.249.68.20
66.249.68.21
66.249.68.24
66.249.68.25
66.249.68.28
66.249.68.30
66.249.72.239
66.249.72.241
66.249.72.243
66.249.74.16
66.249.79.20
66.249.79.133
66.249.79.135
66.249.79.137
66.249.79.139
66.249.79.141
66.249.79.143
66.249.79.145
66.249.79.147
66.249.79.153
66.249.82.16
66.249.82.18
66.249.82.20
66.249.82.26
66.249.82.28
66.249.82.30
66.249.82.176
66.249.82.178
66.249.82.180
66.249.82.186
66.249.82.188
66.249.82.190
66.249.83.17
66.249.83.19
66.249.83.21
66.249.88.40
66.249.88.42
66.249.88.44
66.249.88.45
66.249.88.46
66.249.88.47
72.14.199.68
72.14.199.70
72.14.199.72
72.14.199.73
72.14.199.76
72.14.199.77
72.14.199.79
72.14.199.80
72.14.201.152
72.14.201.153
74.125.150.1
74.125.150.19
74.125.150.20
74.125.150.23
74.125.150.24
74.125.150.25
74.125.150.27
74.125.150.28
74.125.150.31
74.125.212.235
74.125.212.237
74.125.212.239
74.125.212.241
74.125.212.243
74.125.214.16
74.125.214.18
74.125.214.20
74.125.214.26
107.178.234.23
107.178.234.85
107.178.234.88
107.178.234.92
107.178.234.144
107.178.234.147
107.178.234.155
107.178.234.156
107.178.234.158
130.211.54.158
193.186.4.134
193.186.4.152
193.186.4.153
汎用版
これだけだと芸がないので、stringとかu16stringとか一通りの文字列型に対応する汎用版。
関数テンプレートの特殊化ってこういう使い方でいいのかよく分かってないが。
区切り文字の取得以外は、前のコードとほぼ同じ。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
namespace string_types_all { template<typename T> T get_delim() { return 0; } template<> char get_delim<char>() { return '.'; } template<> wchar_t get_delim<wchar_t>() { return L'.'; } template<> char16_t get_delim<char16_t>() { return u'.'; } template<> char32_t get_delim<char32_t>() { return U'.'; } } template<typename T> bool less_ipaddress( const std::basic_string<T>& lhs, const std::basic_string<T>& rhs ) { if( lhs == rhs ) return false; using tstring = std::basic_string<T>; tstring lip = lhs; tstring rip = rhs; const tstring pr( 1, string_types_all::get_delim<T>() ); for( int i = 0; i < 3; ++i ) { // オクテットの長さを比較 const auto l_pos = lip.find( pr ); const auto r_pos = rip.find( pr ); if( l_pos != r_pos ) return l_pos < r_pos; // 区切りか見つからなければ(取得失敗)終了 if( l_pos == tstring::npos && r_pos == tstring::npos ) return false; // 長さが一緒なら中身を比較 const auto l_str = lip.substr( 0, l_pos ); const auto r_str = rip.substr( 0, r_pos ); if( l_str != r_str ) return l_str < r_str; // 中身が一緒なら削除して次へ lip = lip.substr( l_pos + 1 ); rip = rip.substr( r_pos + 1 ); } // 第3オクテットまで同じだった場合 if( lip.length() == rip.length() ) return lip < rip; return lip.length() < rip.length(); } |
呼び出しはこんな感じ。
1 |
std::sort( gbots.begin(), gbots.end(), less_ipaddress<wchar_t> ); |
区切りの文字定数取得は、c++14以降だとbasic_string_viewを使ってもう少しすっきり出来るらしい。
C++Builder10.2.3 / Windows10(22H2)で確認。