C ++: сравнение строк без учета регистра из первых n символов - PullRequest
2 голосов
/ 14 апреля 2011

Мой вопрос похож на this , но у меня есть две строки (как char *), и задача состоит в том, чтобы заменить функцию strnicmp (доступно только для MS VC) на что-то вроде boost::iequals,

Примечание strnicmp - это не stricmp - сравниваются только первые n символов.

Есть ли какое-нибудь решение проще, чем это:

void foo(const char *s1, const char *s2) 
{
    ...

    std::string str1 = s1;
    std::string str2 = s2;
    int n = 7;

    if (boost::iequals(str1.substr(0, n), str2)) {
        ...
    }
}

Ответы [ 6 ]

5 голосов
/ 14 апреля 2011

Если это действительно необходимо, напишите свою собственную функцию:

bool mystrnicmp(char const* s1, char const* s2, int n){
  for(int i=0; i < n; ++i){
    unsigned char c1 = static_cast<unsigned char>(s1[i]);
    unsigned char c2 = static_cast<unsigned char>(s2[i]);
    if(tolower(c1) != tolower(c2))
      return false;
    if(c1 == '\0' || c2 == '\0')
      break;
  }
  return true;
}
4 голосов
/ 14 апреля 2011

Для нечувствительности к регистру вам нужна пользовательская функция сравнения (или функтор):

struct EqIgnoreCase
{
    bool operator()( char lhs, char rhs ) const
    {
        return ::tolower( static_cast<unsigned char>( lhs ) )
            == ::tolower( static_cast<unsigned char>( rhs ) );
    }
};

Если я правильно понимаю, вы проверяете префикс.Самый простой способ сделать это:

bool
isPrefix( std::string const& s1, std::string const& s2 )
{
    return s1.size() <= s2.size()
        && std::equals( s1.begin(), s1.end(), s2.begin(), EqIgnoreCase() );
}

(Обратите внимание на проверку размеров. s1 не может быть префиксом s2, если он длиннее s2. И, конечно,, std::equals будет сталкиваться с неопределенным поведением, если вызывается с s1 длиннее s2.)

2 голосов
/ 14 апреля 2011

Для функции, определенной в терминах C-строк (символьных указателей), переход "вверх" к строкам STL кажется ужасно неэффективным, но, возможно, это совершенно преждевременное мышление с моей стороны.

Я бы рассмотрел прямое решение C«проще», но опять же, это зависит от перспективы.

#include <ctype.h>

void foo(const char *s1, const char *s2)
{
  size_t i, n = 7;

  for(i = 0; i < n; i++)
  {
    if(tolower(s1[i]) != tolower(s2[i]))
      return;
    if(s[i] == '\0' && s2[i] == '\0')
      break;
  }
  /* Strings are equal, do the work. */
  ...
}

Предполагается, что если обе строки заканчиваются до того, как длина префикса исчерпана, это совпадение.

КонечноВышеприведенное предполагает использование строк ASCII, в которых tolower() имеет смысл.

1 голос
/ 14 апреля 2011

Я не знаю, считается ли это более простым или нет, но в нем меньше строк, и скорость должна быть довольно хорошей.

#include <boost/iterator/transform_iterator.hpp>
#include <algorithm>
#include <cctype>

bool equal_insensitive_n( char const *a, char const *b, size_t n ) {
    n = std::min( n, std::min( ::strlen( a ) + 1, ::strlen( b ) + 1 ) );
    #define tilc(S) boost::make_transform_iterator( (S), ::tolower )
    return std::equals( tilc(a), tilc(a) + n, tilc(b) );
    #undef tilc
}
1 голос
/ 14 апреля 2011

что-то вроде этого должно работать ..

#include <iostream>
#include <string>
#include <cctype>
#include <cstring>
#include <algorithm>

struct isequal
{
  bool operator()(int l, int r) const
  {
    return std::tolower(l) == std::tolower(r);
  }
};

bool istrncmp(const char* s1, const char* s2, size_t n)
{
  size_t ls1 = std::strlen(s1);
  size_t ls2 = std::strlen(s2);
  // this is strict, but you can change
  if (ls1 < n || ls2 < n)
    return false;

  return std::equal(s1, s1 + n, s2, isequal()); 
}

int main(void)
{
  std::cout << istrncmp("fooB", "fooA", 3) << std::endl;
  std::cout << istrncmp("fooB", "fooA", 5) << std::endl;
  std::cout << istrncmp("fooB", "f1oA", 3) << std::endl;
  return 0;
}
1 голос
/ 14 апреля 2011

Предлагаю написать функцию самостоятельно, вот так:

bool strnicmp2(const char *s, const char *t, size_t n) {
    while (n > 0 && *s && *t && tolower(*s) == tolower(*t)) {
        ++s;
        ++t;
        --n;
    }
    return n == 0 || !*s || !*t;
}
...