首页 > 代码库 > 踏破铁鞋无觅处,得来全不费工夫--删除文本多余空行ClipboardFormatter

踏破铁鞋无觅处,得来全不费工夫--删除文本多余空行ClipboardFormatter

从网上拷一些文章,总是有很多很多很多....的空行,
如下图(CRLF就是换行符\r\n),怎么去掉它们呢?

一般方法就是贴到文本编辑器中,
然后查找替换,查找替换,查找替换....
周而复始,不厌其烦....
一定有更好的办法,

想象中...

如果有什么办法,让电脑自动完成这件事,那该多好啊!

于是就有了本文的想法:
------写一个程序自动删除拷贝的文本里多余的空行.
(电脑的过剩的计算资源不用白不用)


动手吧

要实现的程序是这样工作的:
首先得取得剪切板的内容(
不要问剪切板是什么?),

然后查找重复的关键字(\r\n\r\n),然后替换成一个(\r\n),

最后把替换完的文本内容,写回剪切板中,
...后面,会考虑写成托盘程序,

能自启动

写代码了^_^

实现一个剪切板的类Clipboard,主要用于取得剪切板内容,和设置剪切板内容

//仅用于个人测试学习用途,作者不对使用本软件与代码产生的任何结果负责
//作者: wisepragma
//主页:http://blog.csdn.net/wisepragma

#pragma once
#include <Windows.h> 
#include <tchar.h>
class Clipboard
{
private:
        TCHAR*          m_pBuffer;//动态内存,用于存剪切板文本拷贝
        int             m_bufSize;//拷贝的缓冲大小
        UINT            m_ClipFormat;//剪切板文本的格式,根据程序自动选择
        HANDLE          m_hclip;//剪切板句柄,实际是剪切板内存指针
private:
        void destroy()
        { 
                if(m_pBuffer!=NULL) 
                { 
                        delete[]m_pBuffer;
                        m_pBuffer=NULL;
                }
        }
public:
        Clipboard()
        {
                m_pBuffer=NULL;
                m_ClipFormat=(  sizeof(TCHAR)==sizeof(WCHAR) )?CF_UNICODETEXT:CF_TEXT;//根据程序版本,设置剪切板的文本类型
        }
        ~Clipboard()
        { 
                destroy();
        }      
        UINT count()
        {
                m_bufSize=0;
                if(OpenClipboard(NULL))
                {
                        if(IsClipboardFormatAvailable(m_ClipFormat))
                        {		
                                HANDLE hclip=GetClipboardData(m_ClipFormat);

                                TCHAR* pClipBuffer=static_cast<TCHAR*>(GlobalLock(hclip) );
                                m_bufSize=lstrlen(pClipBuffer);
                                GlobalUnlock(hclip);
                                CloseClipboard();
                        }
                }
                return m_bufSize;
        }
        TCHAR* gettxt()
        {
                destroy();//释放动态内存,避免多次调用下内存泄漏
                if(OpenClipboard(NULL))
                {
                        if(IsClipboardFormatAvailable(m_ClipFormat))
                        {
                                HANDLE hclip=GetClipboardData(m_ClipFormat);
                                TCHAR* pClipBuffer=static_cast<TCHAR*>(GlobalLock(hclip) );//锁定剪切板,取得剪切板字符串地址 
                                if(m_pBuffer!=NULL) delete[]m_pBuffer;
                                m_bufSize=lstrlen(pClipBuffer);//并计算此字符串长度
                                m_pBuffer=new TCHAR[m_bufSize+1];//申请动态内存,加1个空间,装NULL结尾符
                                memcpy(m_pBuffer,pClipBuffer,m_bufSize*sizeof(TCHAR));//拷出剪切板,bugfixed:memcpy()以字节计数不是字
                                m_pBuffer[m_bufSize]=TEXT(‘\0‘);//组装成一个以NULL结尾的字符串
                                GlobalUnlock(hclip);//解锁剪切板,锁与解锁间动作要快
                                CloseClipboard();
                        }
                }
                return m_pBuffer;
        }
        bool settxt(TCHAR* str)
        { 
                HANDLE hClip;
                if(m_hclip!=NULL) GlobalFree( m_hclip);
                if( OpenClipboard(NULL) )
                {
                        EmptyClipboard();//写入前一定要清空
                        int len=lstrlen(str);
                        m_hclip=GlobalAlloc(GMEM_MOVEABLE,(len+1)*sizeof(TCHAR));//BUBFIXED:GlobalAlloc()参数2以字节为单位
                        TCHAR *pClipBuffer=static_cast<TCHAR*>(GlobalLock(m_hclip));
                        lstrcpy(pClipBuffer,str);
                        //写入剪切板
                        GlobalUnlock(m_hclip);
                        hClip=SetClipboardData(m_ClipFormat,m_hclip);
                        //成功反回指向剪切板的句柄,失败返回NULL
                        CloseClipboard();
                }
                return (hClip!=NULL);//写入成功
        }
};

接下来,要来怎么处理文本字符串
这个问题好像很简单,不过我想得太复杂了...

举例来说有一个字符串是这样定义的

CString s="今天今天今天今天今天今天我有所思在远道,一日不见兮,我心悄悄今天今天今天今天今天今天今天今天今天今天今天今天常言道:天道酬勤,静以修身,俭以养德.今天今天今天事事通晓皆道理,人情达练即文章今天今天今天今天今天今天";

我们要把其中所有的,多个连续的"今天"替换成一个"今天",于是我想到从字串开头逐个字遍历,当找到"今天"后,以关键字"今天"的长度遍历,直到找到非关键字"今天"记录下关键字串的开头与结尾,重复个数等信息,然后再逐个字遍历,直到字串的结尾(描述好长)


struct node 
{
        UINT iDifferentStart;
        UINT nSame;
        UINT iSameStart;
        node *next;
};

class KeyWordReplacer
{
private:
        node *m_head;//单向链表记录下索引等信息
        CString *m_sString;
        CString m_sKeyWord4Search;
        CString m_sKeyWord4Replace;

        CString m_sFormatText;
        UINT m_nString;
        UINT m_nKeyWord;

public:
        ~KeyWordReplacer()
        {
                destory();
        }
        void destory()
        {
                if(NULL!=m_head)
                {
                        while(m_head!=NULL)
                        {
                                node *idx=m_head;
                                m_head=m_head->next;
                                delete idx;
                        }
                }
                m_head=NULL;
        }
        KeyWordReplacer( CString  *sTxt, TCHAR *pSearchkeyWord,TCHAR *pReplaceKeyWord=NULL)
        {                
                //循环的工作原理:从m_sString中查找与关键字m_sKeyWord4Search相同的,记录相同的个数
                //然后在返回的索引下查找与关键字m_sKeyWord4Search不同的
                //通过单向链表记录下索引等信息
                m_sString=sTxt;
                m_sKeyWord4Search=pSearchkeyWord;
                m_sKeyWord4Replace=pReplaceKeyWord;
                m_nString=m_sString->GetLength();
                m_nKeyWord=m_sKeyWord4Search.GetLength();
                m_head=NULL;

                UINT ix=m_sString->Find(m_sKeyWord4Search);//ix用于从m_sString查找关键字m_sKeyWord4Search
                if( ix!=-1 )//没找到返回-1,找到返回第一个关键字下标
                {
                        m_head=new node;
                        m_head->next=NULL;
                        m_head->nSame=0; //用于累计相同关键字的个数
                        if(ix==0) m_head->iDifferentStart=-1;//在开头找到关键字, m_head->iDifferentStart以-1表示
                        else m_head->iDifferentStart=0;//在开头没找到关键字, m_head->iDifferentStart以0表示
                        m_head->iSameStart=ix;// m_head->iSameStart即iDifferentEnd


                        node *inx=m_head;
                        do{
                                UINT iz=ix;//iz用于记录查找非关键字退出循环时刻,最后的索引
                                for(UINT iy=ix; iy<=m_nString-m_nKeyWord; iy+=m_nKeyWord)//iy用于从m_sString查找非m_sKeyWord4Search关键字
                                {
                                        iz=iy;

                                        if( m_sString->Mid(iy,m_nKeyWord)==m_sKeyWord4Search )
                                        {
                                                inx->nSame++; //累计相同关键字的个数
                                                if(inx->nSame==1) inx->iSameStart=iy;//第一次找到关键字即iDifferentEnd
                                                //_tprintf(_T("//[%d],%d,%s\n"),iy,inx->nSame,m_sString->Mid(iy,m_nKeyWord));
                                        }
                                        else
                                        {     //链表有两种生长方式(new node<==m_head 和 m_head==>new node) //本程序使用此方式m_head==>new node
                                                inx->next=new node;
                                                inx=inx->next;
                                                inx->nSame=0;//初始化为0,以便下一次累计相同关键字的个数
                                                inx->next=NULL;//重要!!!这是遍历结束的标志
                                                inx->iSameStart=-1;//在结尾没找到关键字,iSameStart=-1表示
                                                inx->iDifferentStart=iy;//链表记录非关键字的开始
                                                //_tprintf(_T("\n"));
                                                break;//找到不同的即立退出
                                        }
                                }					 
                                ix=m_sString->Find(m_sKeyWord4Search,iz);//查找关键字

                        }while( ix!=-1 &&  ix<m_nString-m_nKeyWord);

                }

        }
        CString &Replace(TCHAR *pReplaceKeyWord=NULL)
        {
                if( NULL!=pReplaceKeyWord ) m_sKeyWord4Replace=pReplaceKeyWord;
                if(NULL!=m_head)
                {
                        node *idx=m_head;
                        while(idx!=NULL )
                        {
                                _tprintf(_T("//[iDifferentStart:%d],iSameStart:%d,[nSame:%d],%s\n"),idx->iDifferentStart,idx->iSameStart, idx->nSame,m_sString->Mid(idx->iDifferentStart) );

                                if( idx->iSameStart == -1 )
                                {
                                        m_sFormatText+=m_sString->Mid( idx->iDifferentStart);
                                        //m_sFormatText+=m_sKeyWord4Replace;//注释掉末尾不加入替换关键字
                                        _tprintf(_T("//....[sDifferent:%s]\n"),m_sString->Mid( idx->iDifferentStart) );
                                }
                                else 
                                {                                      
                                        if(idx->iDifferentStart != -1)
                                        {
                                                m_sFormatText+=m_sString->Mid(idx->iDifferentStart, idx->iSameStart - idx->iDifferentStart);
                                                m_sFormatText+=m_sKeyWord4Replace;
                                                _tprintf(_T("//....[sDifferent:%s]\n"), m_sString->Mid(idx->iDifferentStart, idx->iSameStart - idx->iDifferentStart) );
                                        }
                                }
                                idx=idx->next;
                        }
                }
                _tprintf(_T("字串:%s\n查找:%s\n替换成:%s\n结果:%s\n"),*m_sString,m_sKeyWord4Search,m_sKeyWord4Replace,m_sFormatText);

                return m_sFormatText;
        }
        TCHAR *getText()
        {
                return m_sFormatText.GetBuffer( m_sFormatText.GetLength() );
        }
};


int _tmain()
{	
        setlocale(LC_CTYPE,"CHS");//加上#include<locale.h>,[加上#include <tchar.h>]让_tprintf()在ANSI,UNICODE下都能支持中文

        //CString s="我是菩提树上菩提花,冷眼看人世千年尘沙,你流连树下,回眸那一刹,天地间只剩你眉眼如画,长亭十里忆你风袖迎晨霞,清酒一壶醉里弄琴琶,长亭十里忆你薄衫牵骏马,梅雨一帘多少相思话";
        //CString s="今天今天今天今天今天今天今天今天今天";
        //CString s=_T("但是今天今天今天今天今天它真写今天今天今天些什么着");
        //CString s="今天今天今天今天今天今天我有所思在远道,一日不见兮,我心悄悄今天今天今天今天今天今天今天今天今天今天今天今天常言道:天道酬勤,静以修身,俭以养德.今天今天今天事事通晓皆道理,人情达练即文章今天今天今天今天今天今天";

        Clipboard brd;     
        CString sClipBoard=brd.gettxt(); 
        KeyWordReplacer crlf(&sClipBoard,_T("\r\n") );
        crlf.Replace(_T("\r\n"));
        //KeyWordReplacer crlf(&sClipBoard,_T("今天"),_T("▓") );
        if(NULL!=crlf.getText() ) 
        {

                brd.settxt( crlf.getText() );
        }
        else _tprintf(_T("没有找到和替换\n"));

        getchar();
}




下面就来测试


结果是这样的


但是当我把一个4MB左右的文本拿来作试验的时候,发现了一个问题
那就是---效率实在太低了!!!
辗转反侧,上下求索,没找到好的办法.....

突然有一天,眼前一亮,CString 不是有一个Replace()函数吗
然后就着NOTEPAD2替换着文本的换行


然后简化了代码:(有点黯然神伤)

bool bRemoveAllCRLF=false;
Clipboard brd;    
_tprintf(_T("...正在读取剪切板...\n"));
CString sClipBoard=brd.gettxt(); 
_tprintf(_T("...正在查找并替换多余空行...\n"));
int nReplace=0;
bool bKeyWordFound=false;
do{
	   if(bRemoveAllCRLF)
	   {
		   nReplace=sClipBoard.Replace(_T("\r\n"),_T(""));      
	   }
	   else
	   {
		   nReplace=sClipBoard.Replace(_T("\r\n\r\n"),_T("\r\n"));      
	   }
	   if( nReplace>0 )bKeyWordFound=true;
}while( nReplace>0);//重复替换,直到替换个数为0
if(bKeyWordFound ) 
{            
		_tprintf(_T("...正在写入剪切板...\n"));
		bool bSucceed=brd.settxt( sClipBoard.GetBuffer(sClipBoard.GetLength() ) );                  
	  if(bSucceed) _tprintf(_T("...删除剪切板多余空行成功...\n"));
}

虽然是重复地进行查找替换,但是它的效率不是一般地高,差距啊

接着把KeyWordReplacer类改进为KeyWordRemover

//仅用于个人测试学习用途,作者不对使用本软件与代码产生的任何结果负责
//作者: wisepragma
//主页:http://blog.csdn.net/wisepragma
#pragma once
#include <windows.h>
#include <tchar.h>
class KeyWordRemover
{
private:
        CString m_sText;
public:
	bool remove( TCHAR  *sTxt, TCHAR *pskeyWord,bool bRemoveAll=false)
	{                
        
                CString s1keyword(pskeyWord);//单重关键字
                CString s2keyword=s1keyword+pskeyWord;//双重关键字
                m_sText=sTxt;
                int nReplace=0;
                bool bKeyWordFound=false;
                do{
                        if(bRemoveAll)
                        {
                                nReplace=m_sText.Replace(s1keyword,_T(""));    //s1keyword单重关键字 替换成 空即删除关键字 , 这个比下面的耗时 
                        }
                        else
                        {
                                nReplace=m_sText.Replace(s2keyword,s1keyword);        //双重关键字 替换成 单重关键字 即 缩减关键字  
                        }
                        if( nReplace>0 )bKeyWordFound=true;
                }while( nReplace>0);//直到替换个数为0
              
                return bKeyWordFound;
	}
        TCHAR *gettxt()
        {
                return m_sText.GetBuffer( m_sText.GetLength() );
        }
};

搞定了!

做成WINDOWS托盘图标式软件是这个样子的




代码与软件
ClipboardFormatter[bin]  
ClipboardFormatter[src]