C语言中的SQLite和支持REGEXP

我在C使用sqlite3 ,我想添加对REGEXP操作符的支持。 默认情况下,用户定义函数regexp()不存在,调用REGEXP通常会导致错误(根据SQLite页面)。

  • 如何添加正则regexp函数来支持REGEXP ? 据推测,我会通过sqlite3_create_function调用来做到这一点,但我不知道应用程序定义的regexp()会是什么样子。

  • 我可以使用regex.h的函数与sqlite3_create_function以及如何? 我传递给SQLite的任何函数都必须带有三个类型为sqlite3_context *,int,sqlite3_value **的参数。 但是,SQLite文档似乎没有解释这些参数的含义。

  • 是否有一个C regexp()函数的示例代码?

  • 我无法通过Google或SQLite页面找到很多关于此的信息。


    你也可以试试这个:

    #include <regex.h>
    

    ...

    void sqlite_regexp(sqlite3_context* context, int argc, sqlite3_value** values) {
        int ret;
        regex_t regex;
        char* reg = (char*)sqlite3_value_text(values[0]);
        char* text = (char*)sqlite3_value_text(values[1]);
    
        if ( argc != 2 || reg == 0 || text == 0) {
            sqlite3_result_error(context, "SQL function regexp() called with invalid arguments.n", -1);
            return;
        }
    
        ret = regcomp(&regex, reg, REG_EXTENDED | REG_NOSUB);
        if ( ret != 0 ) {
            sqlite3_result_error(context, "error compiling regular expression", -1);
            return;
        }
    
        ret = regexec(&regex, text , 0, NULL, 0);
        regfree(&regex);
    
        sqlite3_result_int(context, (ret != REG_NOMATCH));
    }
    

    ...

    sqlite3_create_function(*db, "regexp", 2, SQLITE_ANY,0, &sqlite_regexp,0,0)
    

    它看起来像这样:

    static void user_regexp(sqlite3_context *context, int argc, sqlite3_value **argv)
    {
        struct re_pattern_buffer buffer;
        const char *out;
        char *pattern;
        char *input_string;
        char *result;
        struct re_registers regs;
    
        if ((sqlite3_value_type(argv[0]) != SQLITE_TEXT )
             || ((sqlite3_value_type(argv[1]) != SQLITE_TEXT ))
        {
            sqlite3_result_err("Improper argument types");
            return;
        }
    
        re_set_syntax(RE_SYNTAX_POSIX_EGREP);
        memset(&buffer, 0, sizeof (buffer));
        if (!(pattern = strdupa(sqlite3_value_text(argv[0])))
            || !(input_string = strdupa(sqlite3_value_text(argv[1]))))
        {
            sqlite3_result_err_nomem("Could not allocate memory for strings");
            return;
        }
    
        if ((out = re_compile_pattern(pattern, strlen(pattern), &buffer))
        {
            sqlite3_result_err("Could not compile pattern!");
            return;
        }
    
        if (re_match(&buffer, input_string, strlen(input_string), 0, &regs) < 0) 
            sqlite3_result_int64(context, 0);
        else 
        {
            result = strndupa(input_string + regs.start[0], regs.end[0] - regs.start[0]);    
            sqlite3_result_text(context, result, NULL, SQLITE_TRANSIENT);
        }
    }
    

    好吧,对于这个有点太迟了,但是我很想将这个发布给那些正在使用C ++ Wrapper的C语言程序员,比如我正在使用的[SQLiteCpp]。 这个答案假设你使用SQLiteCpp。

  • 从[这里]安装Regex for windows二进制文件。 这给你足够的文件,即regex.h包含文件和regex2.dll 。 请记住在项目中添加路径regex.h,并在包含客户端可执行文件的文件夹中拥有dll的副本。
  • 在构建[SQLiteCpp]之前,我们需要进行一些更改以将正则表达式功能添加到SELECT查询。 为此,从[SQLiteCpp]项目中打开Database.cpp文件

  • 包含Regex for windows的regex.h头文件
  • 在所有包含之后,在下面添加(当然,您可以自定义以适应您的需求!)它下面的一段代码。

    extern "C" {
    void sqlite_regexp(sqlite3_context* context, int argc, sqlite3_value** values) {
    int ret;
    regex_t regex;
    char regtext[100];
    char* reg = (char*)sqlite3_value_text(values[0]);
    sprintf(regtext, ".*%s.*", reg);
    //printf("Regtext : %s", regtext);
    char* text = (char*)sqlite3_value_text(values[1]);
    /*  printf("Text : %sn", text);
    printf("Reg : %sn", reg); */
    if (argc != 2 || reg == 0 || text == 0) {
        sqlite3_result_error(context, "SQL function regexp() called with invalid arguments.n", -1);
        return;
    }
    
    ret = regcomp(&regex, regtext, REG_EXTENDED | REG_NOSUB | REG_ICASE);
    if (ret != 0) {
        sqlite3_result_error(context, "error compiling regular expression", -1);
        return;
    }
    
    ret = regexec(&regex, text, 0, NULL, 0);
    /*  if (ret == 0) {
    printf("Found a match. Press any key to continue");
    getc(stdin);
    }*/
    regfree(&regex);
    
    sqlite3_result_int(context, (ret != REG_NOMATCH));
    }
    }
    
  • 现在该更改文件中定义的构造函数了。 改变如下所示的那些。

    // Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags.
    Database::Database(const char* apFilename,
    const int   aFlags /*= SQLite::OPEN_READONLY*/,
    const int   aBusyTimeoutMs/* = 0 */,
    const char* apVfs/*= NULL*/) :
    mpSQLite(NULL),
    mFilename(apFilename)
    {
    const int ret = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, apVfs);
    //std::cout << "Reached here";
    //sqlite3_create_function_v2(mpSQLite, "REGEXP", 2, SQLITE_ANY,&sqlite_regexp, NULL, NULL, NULL,NULL);
    sqlite3_create_function(mpSQLite, "regexp", 2, SQLITE_ANY, 0, &sqlite_regexp, 0, 0);
    if (SQLITE_OK != ret)
    {
    const SQLite::Exception exception(mpSQLite, ret); // must create before closing
    sqlite3_close(mpSQLite); // close is required even in case of error on opening
    throw exception;
    }
    else {
    
    }
    if (aBusyTimeoutMs > 0)
    {
    setBusyTimeout(aBusyTimeoutMs);
    }
    }
    
    // Open the provided database UTF-8 filename with SQLite::OPEN_xxx provided flags.
    Database::Database(const std::string& aFilename,
    const int          aFlags     /*    = SQLite::OPEN_READONLY*/,
    const int          aBusyTimeoutMs/*  = 0*/,
    const std::string& aVfs/* = "" */) :
    mpSQLite(NULL),
    mFilename(aFilename)
    {
    
    const int ret = sqlite3_open_v2(aFilename.c_str(), &mpSQLite, aFlags, aVfs.empty() ? NULL : aVfs.c_str());
    sqlite3_create_function(mpSQLite, "regexp", 2, SQLITE_ANY, 0, &sqlite_regexp, 0, 0);
    if (SQLITE_OK != ret)
    {
    const SQLite::Exception exception(mpSQLite, ret); // must create before closing
    sqlite3_close(mpSQLite); // close is required even in case of error on opening
    throw exception;
    }
    if (aBusyTimeoutMs > 0)
    {
    setBusyTimeout(aBusyTimeoutMs);
    }
    }
    
  • 到目前为止,你的sqlite有一些严肃的regex能力。 只需建立该项目。

  • 编写一个客户端程序来测试功能。 它可以像下面这样(从SQLiteCpp示例中借用而不会感到羞耻)。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <string>
    #include <SQLiteCpp/SQLiteCpp.h>
    #include <SQLiteCpp/VariadicBind.h>
    // Notice no sqlite3.h huh?
    // Well, this is a C++ wrapper for the SQLITE CAPI afterall.
    
    #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER
    namespace SQLite
    {
    /// definition of the assertion handler enabled when SQLITECPP_ENABLE_ASSERT_HANDLER is defined in the project (CMakeList.txt)
    void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg)
    {
    // Print a message to the standard error output stream, and abort the program.
    std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message "" << apMsg << ""n";
    std::abort();
    }
    }
    #endif
    
    /// Get example path
    static inline std::string getExamplePath()
    {
    std::string filePath(__FILE__);
    return filePath.substr(0, filePath.length() - std::string("Client.cpp").length());
    }
    
    /// Example Database
    static const std::string filename_example_db3 = getExamplePath() + "/example.db3";
    /// Image
    static const std::string filename_logo_png = getExamplePath() + "/logo.png";
    
    
    /// Object Oriented Basic example
    class Example
    {
    public:
    //Constructor
    Example() :
    mDb(filename_example_db3),
    // User change the db and tables accordingly
    mQuery(mDb, "SELECT id,name FROM lookup WHERE name REGEXP :keyword")
    // Open a database file in readonly mode
    {       
    }
    virtual ~Example()
    {
    }
    
    /// List the rows where the "weight" column is greater than the provided aParamValue
    void namehaskeyword(const std::string searchfor)
    {
    std::cout << "Matching results for " << searchfor << "n";
    
    // Bind the integer value provided to the first parameter of the SQL query
    mQuery.bind(1,searchfor); // same as mQuery.bind(1, aParamValue);
    
     // Loop to execute the query step by step, to get one a row of results at a time
    while (mQuery.executeStep())
    {
    std::cout<<mQuery.getColumn(0) << "t" << mQuery.getColumn(1) << "n";
    }
    
    // Reset the query to be able to use it again later
    mQuery.reset();
    }
    
    private:
    SQLite::Database    mDb;    ///< Database connection
    SQLite::Statement   mQuery; ///< Database prepared SQL query
    };
    
    int main()
    {
    // Using SQLITE_VERSION would require #include <sqlite3.h> which we want to avoid: use SQLite::VERSION if possible.
    // std::cout << "SQlite3 version " << SQLITE_VERSION << std::endl;
    std::cout << "SQlite3 version " << SQLite::VERSION << " (" << SQLite::getLibVersion() << ")" << std::endl;
    std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl;
    
    try
    {
    // Doing  a regex query.
    Example example;
    char wannaquit = 'n';
    std::string keyword;
    // Deliberate unlimited loop. You implement something sensible here.
    while (wannaquit != 'y') {
    // Demonstrates the way to use the same query with different parameter values
    std::cout << "Enter the keyword to search for : ";
    std::getline(std::cin, keyword);
    example.namehaskeyword(keyword);
    }
    }
    catch (std::exception& e)
    {
    std::cout << "SQLite exception : " << e.what() << std::endl;
    return EXIT_FAILURE; // unexpected error : exit the example program
    }
    return EXIT_SUCCESS;
    }
    

  • 注意:这假定数据库与cpp位于同一个文件夹中

    链接地址: http://www.djcxy.com/p/52915.html

    上一篇: SQLite in C and supporting REGEXP

    下一篇: multithreading