在C语言中使用SQLite并支持正则表达式

8

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

  1. 如何添加一个regexp函数来支持REGEXP?我想通过sqlite3_create_function调用来实现这一点,但我不知道应用程序定义的regexp()应该是什么样子的。

  2. 我能否使用从regex.h中提取的函数与sqlite3_create_function一起使用,应该如何使用?传递给SQLite的任何函数都必须采用三个参数:sqlite3_context *,int,sqlite3_value **类型。然而,SQLite文档似乎没有解释这些参数的含义。

  3. 是否有C语言regexp()函数的示例代码?

我在Google或SQLite页面上没有找到太多相关信息。


用户函数的相关内容在这里解释:http://www.sqlite.org/c3ref/create_function.html - mu is too short
@mu:谢谢,这是我去的第一个地方。顺便问一下,在这里用户函数可以返回什么整数值?还是必须为空? - Ken
1
所有的原型都有 void 返回值,因此可以通过 sqlite3_context 参数报告“真正”的返回值。 - mu is too short
@mu:谢谢,我可能已经接近成功了。看起来我需要在我的“regex”方法结束时调用sqlite3_result_int来通知SQLite结果。虽然我可能错了。 - Ken
希望你已经接近完成了,我对SQLite内部的知识已经到达了极限 :) - mu is too short
1
我刚看到这个问题可能对你有用:https://dev59.com/dVjUa4cB1Zd3GeqPNxAY - mu is too short
3个回答

7
你也可以尝试这个方法:
#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)

1
它将会看起来像这样:

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);
    }
}

0

好的,这可能有点晚了,但我还是想为所有使用C++包装器来使用C SQLITE API的人发布此帖,比如我正在使用的[SQLiteCpp]。本答案假设您使用SQLiteCpp

这里安装Windows的正则表达式二进制文件。这将提供足够的文件,包括regex.h头文件和regex2.dll动态链接库。记得在项目中添加regex.h的路径,并将dll文件复制到包含客户端可执行文件的文件夹中。
在构建SQLiteCpp之前,我们需要对SELECT查询添加正则表达式功能。打开SQLiteCpp项目中的Database.cpp文件,并进行以下更改:
- 从Regex for windows中引用regex.h头文件 - 在所有的#include之后,添加如下代码(根据需要进行自定义):
```cpp 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); 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, 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); regfree(&regex); sqlite3_result_int(context, (ret != REG_NOMATCH)); } } ```
现在是时候修改文件中定义的构造函数了。按照下面所示进行更改:
```cpp 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); sqlite3_create_function(mpSQLite, "regexp", 2, SQLITE_ANY, 0, &sqlite_regexp, 0, 0); if (SQLITE_OK != ret) { const SQLite::Exception exception(mpSQLite, ret); sqlite3_close(mpSQLite); throw exception; } if (aBusyTimeoutMs > 0) { setBusyTimeout(aBusyTimeoutMs); } }
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); sqlite3_close(mpSQLite); throw exception; } if (aBusyTimeoutMs > 0) { setBusyTimeout(aBusyTimeoutMs); } } ```
到此为止,你的SQLite现在具备了强大的正则表达式功能。现在构建项目即可。
编写一个客户端程序来测试功能。可以参考以下示例(从SQLiteCpp Example借用):
```cpp #include #include #include #include #include #include #ifdef SQLITECPP_ENABLE_ASSERT_HANDLER namespace SQLite { void assertion_failed(const char* apFile, const long apLine, const char* apFunc, const char* apExpr, const char* apMsg) { std::cerr << apFile << ":" << apLine << ":" << " error: assertion failed (" << apExpr << ") in " << apFunc << "() with message \"" << apMsg << "\"\n"; std::abort(); } } #endif
static inline std::string getExamplePath() { std::string filePath(__FILE__); return filePath.substr(0, filePath.length() - std::string("Client.cpp").length()); }
class Example { public: Example() : mDb(filename_example_db3), mQuery(mDb, "SELECT id,name FROM lookup WHERE name REGEXP :keyword") { } virtual ~Example() { }
void namehaskeyword(const std::string searchfor) { std::cout << "Matching results for " << searchfor << "\n";
mQuery.bind(1,searchfor);
while (mQuery.executeStep()) { std::cout<
mQuery.reset(); } private: SQLite::Database mDb; SQLite::Statement mQuery; };
int main() { std::cout << "SQlite3 version " << SQLite::VERSION << " (" << SQLite::getLibVersion() << ")" << std::endl; std::cout << "SQliteC++ version " << SQLITECPP_VERSION << std::endl;
try { Example example; char wannaquit = 'n'; std::string keyword; while (wannaquit != 'y') { 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; } return EXIT_SUCCESS; } ```

注意:这假设数据库与您的cpp文件在同一个文件夹中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接