【问题标题】:Extract substrings of a filename提取文件名的子字符串
【发布时间】:2013-05-12 08:19:52
【问题描述】:

在 C/C++ 中,如何从 c:\Blabla - dsf\blup\AAA - BBB\blabla.bmp 中提取子字符串 AAABBB

即在文件名的最后一个文件夹中提取- 之前和之后的部分。

提前致谢。

(PS:如果可能的话,没有Framework .net之类的东西,我很容易迷路)

【问题讨论】:

  • 这可能是使用正则表达式的好例子...

标签: c++ windows string substring


【解决方案1】:

使用 std::string rfind rfind (char c, size_t pos = npos)

  1. 使用 rfind (pos1) 从末尾查找字符“\”
  2. 使用 rfind (pos2) 查找下一个字符“\”
  3. 获取位置 pos2 和 pos1 之间的子字符串。使用 substring 函数。
  4. 查找字符'-' (pos3)
  5. 在 pos3 和 pos1、pos3 和 pos2 之间提取 2 个子字符串
  6. 删除子字符串中的空格。

生成的子字符串将是 AAA 和 BBB

【讨论】:

  • 很好的解决方案。提取没有空格的子字符串可能会更容易。
  • @gkovacs:如果我们知道文本格式总是 word-word ,那么我们可以在提取子字符串时减少/增加位置,这意味着没有空格。
  • 是的,这正是我要说的,所以你的算法中不需要第 6 步。可以用一个语句来解决。但这只是一个小修正,您的解决方案效果很好。
  • 这会起作用,但相当专业。如果原始发帖人没有使这个特定问题变得微不足道的通用工具,他应该将它们添加到他的工具箱中,而不是编写一些不能在其他地方使用的专门代码。
  • 谢谢!你的步骤很好,我认为它会完美运行......但我可能无法轻松实现它;)
【解决方案2】:
#include <iostream>
using namespace std;

#include <windows.h>
#include <Shlwapi.h> // link with shlwapi.lib

int main()
{
    char buffer_1[ ] = "c:\\Blabla - dsf\\blup\\AAA - BBB\\blabla.bmp"; 
    char *lpStr1 = buffer_1;

    // Remove the file name from the string
    PathRemoveFileSpec(lpStr1);
    string s(lpStr1);

    // Find the last directory name
    stringstream ss(s.substr(s.rfind('\\') + 1));

   // Split the last directory name into tokens separated by '-'
    while (getline(ss, s, '-')) 
        cout << s << endl;
}

cmets 中的解释。

这不会修剪前导空格 - 在输出中 - 如果您也想这样做 - 请检查 this

【讨论】:

  • PathRemoveFileSpec 是什么?这也需要写出来。
  • 如果您不在 Windows 下,这将非常有用。 (即使在 Windows 下,这里也没有理由不便携。)
  • 抱歉,我不明白 cmets:这个解决方案是否可移植?
  • @JosBas - 它只能在 Windows 上运行。如果您想让它可移植,请将 PathRemoveFileSpec 替换为另一个子字符串。
【解决方案3】:

这可以通过正则表达式相对轻松地完成: std::regex 如果你有 C++11; boost::regex 如果你不这样做:

static std::regex( R"(.*\\(\w+)\s*-\s*(\w+)\\[^\\]*$" );
smatch results;
if ( std::regex_match( path, results, regex ) ) {
    std::string firstMatch = results[1];
    std::string secondMatch = results[2];
    //  ...
}

另外,你绝对应该拥有splittrim 在工具包中:

template <std::ctype_base::mask test>
class IsNot
{
    std::locale ensureLifetime;
    std::ctype<char> const* ctype;  //  Pointer to allow assignment
public:
    Is( std::locale const& loc = std::locale() )
        : ensureLifetime( loc )
        , ctype( &std::use_facet<std::ctype<char>>( loc ) )
    {
    }
    bool operator()( char ch ) const
    {
        return !ctype->is( test, ch );
    }
};
typedef IsNot<std::ctype_base::space> IsNotSpace;

std::vector<std::string>
split( std::string const& original, char separator )
{
    std::vector<std::string> results;
    std::string::const_iterator current = original.begin();
    std::string::const_iterator end = original.end();
    std::string::const_iterator next = std::find( current, end, separator );
    while ( next != end ) {
        results.push_back( std::string( current, next ) );
        current = next + 1;
        next = std::find( current, end, separator );
    }
    results.push_back( std::string( current, next ) );
    return results;
}

std::string
trim( std::string const& original )
{
    std::string::const_iterator end
        = std::find_if( original.rbegin(), original.rend(), IsNotSpace() ).base();
    std::string::const_iterator begin
        = std::find_if( original.begin(), end, IsNotSpace() );
    return std::string( begin, end );
}

(这些正是您在这里需要的。您显然想要 IsXxx 和 IsNotXxx 谓词的完整补集,拆分 可以根据正则表达式拆分,修剪 可以传递一个谓词对象,指定要做什么 修剪等)

反正splittrim的应用应该很明显了 给你你想要的。

【讨论】:

  • 感谢您的回答!我使用 Visual C++ 2010 express,所以我可能没有 C++11 ?那我需要做什么?我忘了问:如果:a)在最后一个文件夹名称中出现很多` - `,会发生什么情况? b)最后一个文件夹名称中没有出现` - `?
  • 如果你没有 C++11,总会有 boost::regex。而splittrim 不需要c++11。至于如果- 的出现次数不同会发生什么:这取决于。使用我提供的正则表达式,不会有匹配项,因此您不会进入if。使用splittrim,由你决定;当您执行第二个split(在'-' 上)时,您将得到比'-' 多一个字段。
【解决方案4】:

这在纯 C 中完成所有工作和验证:

int FindParts(const char* source, char** firstOut, char** secondOut)
{
const char* last        = NULL;
const char* previous    = NULL;
const char* middle      = NULL;
const char* middle1     = NULL;
const char* middle2     = NULL;
char* first;
char* second;

last = strrchr(source, '\\');
if (!last || (last  == source))
    return -1;
--last;
if (last == source)
    return -1;

previous = last;
for (; (previous != source) && (*previous != '\\'); --previous);
++previous;

{
    middle = strchr(previous, '-');
    if (!middle || (middle > last))
        return -1;

    middle1 = middle-1;
    middle2 = middle+1;
}

//  now skip spaces

for (; (previous != middle1) && (*previous == ' '); ++previous);
if (previous == middle1)
    return -1;
for (; (middle1 != previous) && (*middle1 == ' '); --middle1);
if (middle1 == previous)
    return -1;
for (; (middle2 != last) && (*middle2 == ' '); ++middle2);
if (middle2 == last)
    return -1;
for (; (middle2 != last) && (*last == ' '); --last);
if (middle2 == last)
    return -1;

first   = (char*)malloc(middle1-previous+1 + 1);
second  = (char*)malloc(last-middle2+1 + 1);
if (!first || !second)
{
    free(first);
    free(second);
    return -1;
}

strncpy(first, previous, middle1-previous+1);
first[middle1-previous+1] = '\0';
strncpy(second, middle2, last-middle2+1);
second[last-middle2+1] = '\0';

*firstOut   = first;
*secondOut  = second;

return 1;
}

【讨论】:

  • @James Kanze 在变得聪明(C++/C#)之前,你必须了解普通的 C。也许这是一个家庭作业。
  • 这完全是错误的。在学习 C++ 之前,您应该为纯 C 操心;这只会造成混乱。
  • 在我的日子里,在技术学校,你首先学习 C 是为了简单地了解指针。 “完全错误”?请注意您的语言!
  • 在我上学的时候,你没有学习 C,因为它还没有被发明出来。今天,无论哪个学校,他们都应该在教 C 之前先教 C++(除非他们根本不指望教 C++;例如,如果他们教编程的唯一原因是支持嵌入式控制器等)。除了嵌入式控制器,几乎没有理由教 C。
【解决方案5】:

普通的 C++ 解决方案(没有 boost,也没有 C++11),仍然是 James Kanze (https://stackoverflow.com/a/16605408/1032277) 的正则表达式解决方案是最通用和优雅的:

inline void Trim(std::string& source)
{
size_t position = source.find_first_not_of(" ");
if (std::string::npos != position)
    source = source.substr(position);
position = source.find_last_not_of(" ");
if (std::string::npos != position)
    source = source.substr(0, position+1);
}

inline bool FindParts(const std::string& source, std::string& first, std::string& second)
{
size_t last = source.find_last_of('\\');
if ((std::string::npos == last) || !last)
    return false;

size_t previous = source.find_last_of('\\', last-1);
if (std::string::npos == last)
    previous = -1;

size_t middle = source.find_first_of('-',1+previous);
if ((std::string::npos == middle) || (middle > last))
    return false;

first   = source.substr(1+previous, (middle-1)-(1+previous)+1);
second  = source.substr(1+middle, (last-1)-(1+middle)+1);

Trim(first);
Trim(second);

return true;
}

【讨论】:

  • 谢谢!如果我想提取 ` - ` () 之前和之后的内容,我真的需要trim 吗?如果我们搜索` - `,真的需要修剪吗?
  • 不,你不需要。因此,您可以消除Trim(调用和处理)。
猜你喜欢
  • 2015-09-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多