【问题标题】:Assembly: Replace substring with another string组装:用另一个字符串替换子字符串
【发布时间】:2016-08-17 21:22:19
【问题描述】:

嗨,我是 Assembly 的新手,我在练习时遇到了一些问题,要求我在原始字符串中查找子字符串,如果找到,则将其替换为另一个给定的字符串,但仅适用于发现的奇数事件(1, 3,5...)。即。

  • 原始字符串:“abc world def world”。
  • 要查找的字符串:“world”
  • 字符串替换:“猫”
  • 输出:“acb cat def world”

此外,这些应该适用于所有类型的输入。即使像在这种情况下,“世界”比“猫”长,我也需要完全替换“世界”(“catld”是错误的)。 谁能帮我?!我不知道如何进行。 这是我目前的代码,我仍然没有考虑奇怪的情况,我只是想让它工作:

#include <stdio.h>

void main() {

    //Input
    char stringa[1024] ="This string is a string\n";
    char str1[] ="string";  
    char str2[] ="cat"; 

    //Output
    char result[1024];

    __asm {

        XOR EAX, EAX
        CLD
        LEA ESI, str1
        LEA EDI, stringa
Ciclo:  MOV AL, [ESI]
        REPNZ SCASB
        DEC EDI
        PUSH EDI
        PUSH ESI
        MOV ECX, 7
        REPE CMPSB
        CMP ECX, 0
        POP ESI
        JZ Sost
        JMP Ciclo

Sost:   LEA ESI, str2
        POP EDI
        MOV ECX, 7
        REP MOVSB
    }

    printf("New string: %s\n",result);
}

【问题讨论】:

  • 告诉我们您发布的代码有什么问题。什么都不能代替吗?是不是替换错了?
  • 它甚至不会以它发布的形式编译。 (不确定它是否只是未完成,还是在复制/粘贴过程中被吃掉了。文本编辑器中的 Ctrl+A Ctrl+C 和 stackoverflow 编辑窗口中的 Ctrl+V Ctrl+K 有什么问题?@OP:所以你写了一些指令,但是我没有看到任何算法解释。先写一些英文的cmets它是如何工作的(每个评论应该“简单”到足以被2-10个asm指令覆盖)。
  • 您发布的代码没有任何意义。我建议先用高级语言编写算法,然后将其翻译成汇编。
  • 是的,但即使是有限的功能也是“偶然的”,例如在字符串“ab wo wo world def world”上尝试它,它会崩溃,或者“abc”让它进入无限循环/崩溃等...问题太多了。首先用简单的英语写下你打算做什么的算法。在任何 ASM 之前。输出字符串缓冲区在“short”->“longer”单词这样的情况下会有很大帮助,因为原始字符串太短了。
  • 我的答案的另一个编辑,我分别用“ebx”和“ecx”替换了“i”和“j”,仍然很好用。

标签: string assembly replace substring


【解决方案1】:

下一个代码完成这项工作:它找到并用另一个字符串替换所有(不奇怪)出现的字符串,并将结果字符串留在另一个变量中,在编译器 EMU8086 中对其进行测试(我的 VS 不工作),代码后解释:

.model small
.stack 100h
.data
original db 'abc world def world xyz',0
find     db 'world',0
subst    db 'cat',0
result   db 100 dup(0)
i        dw ?             ;INDEX FOR "ORIGINAL".
j        dw ?             ;INDEX FOR "RESULT".
.code
   mov  ax, @data
   mov  ds, ax
   mov  i, offset original ;"I" POINTS TO "ORIGINAL".
   mov  j, offset result   ;"J" POINTS TO "RESULT".
;SEARCH VARIABLE "FIND" AT CURRENT POSITION ("I").
   mov  si, i
   lea  di, find
search:                        
   mov  al, [di]        ;CURRENT CHAR OF VARIABLE "FIND".
;CHECK IF END OF "FIND".
   cmp  al, 0           ;IF CURRENT CHAR OF "FIND" IS LAST ONE...
   je   match           ;... VARIABLE "FIND" WAS FOUND.
;CHECK IF END OF "ORIGINAL".
   cmp  [si], 0
   je   finale
;CONTINUE.   
   cmp  [si], al        ;CMP ORIGINAL[SI],FIND[DI].
   jne  mismatch        ;CHARS ARE DIFFERENT.
   inc  si              ;NEXT CHAR OF "ORIGINAL".
   inc  di              ;NEXT CHAR OF "FIND".
   jmp  search          ;REPEAT (COMPARE NEXT CHAR).
match:
;WHEN "FIND" IS FOUND, "SUBST" REPLACE IT IN "RESULT".
   mov  i, si           ;SKIP "FIND" IN "ORIGINAL", BUT...
   dec  i               ;...SKIPPED ON CHAR FORWARD (SO DECREASE).
   lea  di, subst       ;STRING TO REPLACE "FIND".
replace:
   mov  al, [di]        ;CURRENT CHAR OF VARIABLE "SUBST".
;CHECK IF END OF "SUBST".
   cmp  al, 0           ;IF CURRENT CHAR OF "FIND" IS LAST ONE...
   je   next
;CONTINUE.
   mov  si, j           ;CURRENT POSITION IN "RESULT".
   mov  [si], al        ;COPY CHAR INTO "RESULT[ J ]".
   inc  j               ;NEXT POSITION IN "RESULT".
   inc  di              ;NEXT POSITION IN "SUBST".
   jmp  replace
mismatch:    
;APPEND CURRENT CHAR (AL) INTO "RESULT".
   mov  si, i           ;CURRENT POSITION IN "ORIGINAL".
   mov  di, j           ;CURRENT POSITION IN "RESULT".
   mov  al, [si]
   mov  [di], al
   inc  j               ;"I" IS ALSO INCREMENTED 4 LINES BELOW.
;NEXT CHAR IN "ORIGINAL".
next:
   lea  di, find        ;SEARCH AGAIN VARIABLE "FIND".
   inc  i               ;NEXT CHAR IN "ORIGINAL".
;CHECK IF END OF "ORIGINAL".
   mov  si, i
   cmp  [si], 0
   jne  search          ;REPEAT (SEARCH "FIND" AGAIN).
;END OF WHOLE PROCESS.
finale:
   mov  ax, 4c00h
   int  21h

结果是:

original = `abc world def world xyz`
find     = `world`
subst    = `cat`
result   = `abc cat def cat xyz`

注意代码中所有字符串(数据段中的变量)都使用0作为结束分隔符。这非常重要,因为这是处理任何大小的字符串的关键,而不是硬编码大小。

现在算法:

  1. 它遍历“原始”中的每个字符。
  2. 访问“original”中的每个字符时都会搜索字符串“find”。
  3. 如果未找到“find”,则将“original”中的当前字符附加到“result”中。
  4. 如果找到“find”,则将字符串“subst”附加到“result”中,并且“i”(指向“original”的指针)跳到搜索循环结束的位置(减 1),以便跳过我们要替换的字符串。 “j”(指向“结果”的指针)从不跳过,它总是加一。

要使其适用于奇数事件,请在标签match: 下添加另一个数值变量作为计数器,如果该变量在标签search: 之后不是奇数,则使其跳转到标签next:

编辑:现在是 Visual Studio 2013 版本(在“C++ Win32 控制台应用程序”上测试):

#include "stdafx.h"
#include "conio.h"

int _tmain(int argc, _TCHAR* argv[])
{  //Input
    char original[1024] = "abc world def world xyz\n";
    char find[]         = "world";
    char subst[]        = "cat";
   //Output
    char result[1024]   = { 0 };
    int i;
    int j;

    _asm { lea  esi, original
           mov  i, esi             // "I" POINTS TO "ORIGINAL".
           lea  esi, result
           mov  j, esi              // "J" POINTS TO "RESULT".
        // SEARCH VARIABLE "FIND" AT CURRENT POSITION ("I").
           mov  esi, i
           lea  edi, find
        search:                        
           mov  al, [edi]          // CURRENT CHAR OF VARIABLE "FIND".
        // CHECK IF END OF "FIND".
           cmp  al, 0              // IF CURRENT CHAR OF "FIND" IS LAST ONE...
           je   match              // ... VARIABLE "FIND" WAS FOUND.
        // CHECK IF END OF "ORIGINAL".
           cmp  [esi], 0
           je   finale
        // CONTINUE.   
           cmp  [esi], al       // CMP ORIGINAL[SI],FIND[DI].
           jne  mismatch        // CHARS ARE DIFFERENT.
           inc  esi              // NEXT CHAR OF "ORIGINAL".
           inc  edi              // NEXT CHAR OF "FIND".
           jmp  search          // REPEAT (COMPARE NEXT CHAR).
        match:
        // WHEN "FIND" IS FOUND, "SUBST" REPLACE IT IN "RESULT".
           mov  i, esi          // SKIP "FIND" IN "ORIGINAL", BUT...
           dec  i               // ...SKIPPED ON CHAR FORWARD (SO DECREASE).
           lea  edi, subst      // STRING TO REPLACE "FIND".
        replace:
           mov  al, [edi]       // CURRENT CHAR OF VARIABLE "SUBST".
        // CHECK IF END OF "SUBST".
           cmp  al, 0           // IF CURRENT CHAR OF "FIND" IS LAST ONE...
           je   next
        // CONTINUE.
           mov  esi, j          // CURRENT POSITION IN "RESULT".
           mov  [esi], al       // COPY CHAR INTO "RESULT[ J ]".
           inc  j               // NEXT POSITION IN "RESULT".
           inc  edi             // NEXT POSITION IN "SUBST".
           jmp  replace
        mismatch:    
        // APPEND CURRENT CHAR (AL) INTO "RESULT".
           mov  esi, i          // CURRENT POSITION IN "ORIGINAL".
           mov  edi, j          // CURRENT POSITION IN "RESULT".
           mov  al, [esi]
           mov  [edi], al
           inc  j               // "I" IS ALSO INCREMENTED 4 LINES BELOW.
        // NEXT CHAR IN "ORIGINAL".
        next:
           lea  edi, find       // SEARCH AGAIN VARIABLE "FIND".
           inc  i               // NEXT CHAR IN "ORIGINAL".
        // CHECK IF END OF "ORIGINAL".
           mov  esi, i
           cmp  [esi], 0
           jne  search          // REPEAT (SEARCH "FIND" AGAIN).
        // END OF WHOLE PROCESS.
        finale:       
    }
    printf("New string: %s", result );
    _getch();
    return 0;
}

编辑#2:以前的代码用变量ij 分别替换为ebxecx(我留下了cmets 说“I”和“J”的部分,所以你可以看到我在哪里做了替换):

#include "stdafx.h"
#include "conio.h"

int _tmain(int argc, _TCHAR* argv[])
{  //Input
    char original[1024] = "abc world def world xyz\n";
    char find[]         = "world";
    char subst[]        = "cat";
   //Output
    char result[1024]   = { 0 };

    _asm { lea  esi, original
           mov  ebx, esi            // "I" POINTS TO "ORIGINAL".
           lea  esi, result
           mov  ecx, esi            // "J" POINTS TO "RESULT".
        // SEARCH VARIABLE "FIND" AT CURRENT POSITION ("I").
           mov  esi, ebx
           lea  edi, find
        search:                        
           mov  al, [edi]          // CURRENT CHAR OF VARIABLE "FIND".
        // CHECK IF END OF "FIND".
           cmp  al, 0              // IF CURRENT CHAR OF "FIND" IS LAST ONE...
           je   match              // ... VARIABLE "FIND" WAS FOUND.
        // CHECK IF END OF "ORIGINAL".
           cmp  [esi], 0
           je   finale
        // CONTINUE.   
           cmp  [esi], al       // CMP ORIGINAL[SI],FIND[DI].
           jne  mismatch        // CHARS ARE DIFFERENT.
           inc  esi             // NEXT CHAR OF "ORIGINAL".
           inc  edi             // NEXT CHAR OF "FIND".
           jmp  search          // REPEAT (COMPARE NEXT CHAR).
        match:
        // WHEN "FIND" IS FOUND, "SUBST" REPLACE IT IN "RESULT".
           mov  ebx, esi        // SKIP "FIND" IN "ORIGINAL", BUT...
           dec  ebx             // ...SKIPPED ON CHAR FORWARD (SO DECREASE).
           lea  edi, subst      // STRING TO REPLACE "FIND".
        replace:
           mov  al, [edi]       // CURRENT CHAR OF VARIABLE "SUBST".
        // CHECK IF END OF "SUBST".
           cmp  al, 0           // IF CURRENT CHAR OF "FIND" IS LAST ONE...
           je   next
        // CONTINUE.
           mov  esi, ecx        // CURRENT POSITION IN "RESULT".
           mov  [esi], al       // COPY CHAR INTO "RESULT[ J ]".
           inc  ecx             // NEXT POSITION IN "RESULT".
           inc  edi             // NEXT POSITION IN "SUBST".
           jmp  replace
        mismatch:    
        // APPEND CURRENT CHAR (AL) INTO "RESULT".
           mov  esi, ebx        // CURRENT POSITION IN "ORIGINAL".
           mov  edi, ecx        // CURRENT POSITION IN "RESULT".
           mov  al, [esi]
           mov  [edi], al
           inc  ecx             // "I" IS ALSO INCREMENTED 4 LINES BELOW.
        // NEXT CHAR IN "ORIGINAL".
        next:
           lea  edi, find       // SEARCH AGAIN VARIABLE "FIND".
           inc  ebx             // NEXT CHAR IN "ORIGINAL".
        // CHECK IF END OF "ORIGINAL".
           mov  esi, ebx
           cmp  [esi], 0
           jne  search          // REPEAT (SEARCH "FIND" AGAIN).
        // END OF WHOLE PROCESS.
        finale:       
    }
    printf("New string: %s", result );
    _getch();
    return 0;
}

【讨论】:

  • 我给定的变量也以 0 结尾,是的,这很重要,谢谢。
  • ...感谢这段代码,我将尝试使用您的一些细节使我的算法工作。如果我失败了,我将使用这个代码(在实验室中我们只在 VS 中工作过,这就是我现在正在使用的)。
  • 我已经更新了我的代码。谢谢,但我现在已经自己完成了!我想这是一个糟糕的代码,但它有效!我尝试了一些不同的输入,它总是返回一个 ggod 输出。明天我会尝试减少代码,因为有很多重复的代码行,但我不知道我是否会成功。我没有告诉你的是我不能添加任何变量,我必须只使用输入一个!不过谢谢,你的代码帮助了我。
  • 坏消息!我认为代码很好,但事实并非如此。如果我在 char[1024] str1 中更改 char[] str1,显然它不起作用。所以我用'/0'改变了代码来查找字符串的结尾,而不是函数LENGTH,这看起来不错。但还是不行! ...
  • ...在我现在的情况下,它不起作用,输入: char[] stringa = "a"(original), char[] str1 = "a"(to find) , char[] str2 = "b"(新字符串)。有一个索引问题,我很确定如果不损坏其他东西,我就无法修复。所以我认为我必须编写一个新算法。正如我之前所说,我不能像 Josè Manuel 那样添加任何变量,使用“i”和“j”来存储指针。我应该像现在一样发布代码吗?但我认为这没有帮助。
【解决方案2】:

我是这样想的: 输入:原始字符串、要查找的子字符串、要替换的子字符串、输出字符串

  1. 将 EDI 设置为原始字符串指向的第一个字节
  2. 将 ESI 设置为要查找的子字符串指向的第一个字节
  3. 移动到 AL 的第一个子字符串字节
  4. 读取原始字符串,直到与给定子字符串的第一个字符不同以查找(SCASB)。
  5. 虽然不同,但将每个字符复制到输出字符串中。
  6. 当/如果它们匹配,将 ECX 设置为子字符串长度。
  7. 比较原始字符串和子字符串,从第一个匹配点开始。
  8. 检查 ECX。
  9. 如果 ECX 为零,我有一个事件,所以转到检查点。
  10. 转到第 4 点。

检查点:a) 递增发生计数器 b) 检查计数器。 c) 如果奇数去更换。 d) 转到第 4 点

替换:a) 将 ECX 设置为替换字符串长度 b) 在输出字符串中复制替换字符串 c) 转到第 4 点

它看起来怎么样?

【讨论】:

  • 听起来很合理。这种对代码如何工作的高级规划正是您在编写实际指令之前应该做的事情,因此您走在正确的轨道上。在实现和调试之后,您可能会发现您的高级推理存在缺陷,但这确实发生了。或者,一旦您开始考虑实现细节,您可能会发现有一种更有效的方式来做事;这也很常见。在这种情况下,您是完成效率较低的方式还是返回并调整高级设计以便在某个地方获得效率取决于您。
  • 谢谢。我不知道,但我写了一个适用于练习要求的代码。几分钟后我会在这里添加代码。
【解决方案3】:

这段代码有效,我尝试了很多输入。有很多重复的代码行,但它有效。后天我看看能不能减。正如我对 Josè 所说,我不能在输入中给出的 4 个字符串中添加变量:stringa(original)、str1(to find)、str2(new substring)、risultato(final output):

    __asm {
                XOR EAX, EAX
                XOR EBX, EBX
                XOR EDX, EDX
                CLD
                LEA EDI, str1
                LEA ESI, stringa

                Cicle : 
                        MOV AL, [ESI]
    //CHECK IF STRING ENDED
                        CMP AL, 0
                        JZ End
    //CHECK THE CHAR
                        SCASB
    //IF THEY DON'T MATCH GO TO COPY CHAR
                        JNZ Append
    //OTHERWISE CHECK IF FROM HERE STRINGS ARE EQUAL
                        MOV ECX, LENGTH str1
                        DEC EDI
                        REPE CMPSB
                        CMP ECX, 0
    //IF YES GO TO CHECK IF IT'S AN ODD OCCURRENCE
                        JZ Checkpoint
    //IF NOT GET BACK ESI AND EDI...
                        NEG ECX
                        ADD ECX, LENGTH str1
                        SUB EDI, ECX
                        SUB ESI, ECX
    //..AND COPY THE SINGLE CHAR
                        MOV risultato[EBX], AL
                        INC EBX
                        INC ESI
                        JMP Cicle

    //COPY CHAR WHEN STRINGS DONT'MATCH    
                Append :
                        INC ESI
                        DEC EDI
                        MOV risultato[EBX], AL
                        INC EBX
                        JMP Cicle

    //CHEK IF ODD/EVEN OCCURRENCE               
                Checkpoint :
                        INC EDX
                        TEST EDX, 00000001h
    //IF ODD GO TO REPLACE ALL SUBSTRING
                        JNZ Replacement
    //OTHERWISE GET BACK ESI/EDI AND COPY THE SAME SUBSTRING TO RESULT
                        SUB EDI, LENGTH str1
                        SUB ESI, LENGTH str1
                        PUSH EDI
                        LEA EDI, risultato[EBX]
                        MOV ECX, LENGTH str1
                        REP MOVSB
    //UPDATE RESULT INDEX COUNTER
                        ADD EBX, LENGTH str1 - 1
    //GET BACK EDI POINTER
                        POP EDI
                        DEC ESI
                        JMP Cicle

    //REPLACEMENT OF THE SUBSTRING                  
                Replacement :
    //GET BACK EDI AT FIRST CHAR AND STORE ESI/EDI FOR NEXT LOOP
                        SUB EDI, LENGTH str1
                        PUSH ESI
                        PUSH EDI
    //SET A POINTER TO THE NEW STRING AND COPY IT TO RESULT
                        LEA ESI, str2
                        LEA EDI, risultato[EBX]
                        MOV ECX, LENGTH str2
                        REP MOVSB
    //UPDATE RESULT COUNTER AND GET BACK EDI/ESI TO PREVIOUS POSITIONS TO CONTINUE
                        ADD EBX, LENGTH str2 - 1
                        POP EDI
                        POP ESI
                        DEC ESI
                        JMP Cicle

                End :
                        MOV risultato[EBX], 0
}

我忘记了一个细节:当我找到 char 'è' 时,这段代码用 ascii 232 而不是 138 表示它(在 AL 中),为什么?我能做些什么? 谢谢

【讨论】:

    【解决方案4】:

    终于好了。我研究了 Jose Manuel 的代码(谢谢),我已经使用 pop/push 而不是变量“i”和“j”对其进行了修改。然后在线测试失败,输入:“ccccc”、“cc”、“f”。结果应该是“fccc”但是是“fcf”。所以我修复了一些其他细节,现在没关系。

            XOR ECX, ECX
            XOR EAX, EAX
            XOR EBX, EBX
            XOR EDX, EDX
            LEA ESI, result
            PUSH ESI                //store result into stack
            LEA ESI, original       //esi points to original
            LEA EDI, find           //edi points to find
    
            Search :
                MOV AL, [EDI]
                    CMP AL, 0           //chek if the end of find
                    JE Match            //if yes, we have an occurrence
                    CMP[ESI], 0         //check if end of original
                    JE Mismatch         //if yes, coopy last char going to Mismatch
                    CMP[ESI], al        //check if char match
                    JNE Mismatch        //if yes go to Mismatch
                    INC ESI             //else repeat Search to check if next char match
                    INC EDI
                    INC ECX             //chars counter
                    JMP Search
    
            Match :
                INC EBX                 //occurrences counter
                    TEST EBX, 00000001h
                    JZ Mismatch         //if even go to Mismatch else continue...
                    LEA EDI, str2       //else point to new string to replace
                    MOV EDX, ESI        //copy the current original pointer
                    POP ESI             //store it also into the stack
    
            Replacement :
                MOV AL, [EDI]           //copy new string char by char
                    CMP AL, 0
                    JE SetRegister      //if end of new string, get back the pointers
                    MOV[ESI], AL        //else continue...
                    INC ESI
                    INC EDI
                    JMP Replacement
    
            Mismatch :
                POP EDI                 //edi points to result
                    SUB ESI, ECX        //set esi to the char it was pointing before Search or Match
                    INC ECX             //shows how many chars we need to replace
                    REP MOVSB           //ready to copy chars from original to result
                    PUSH EDI
                    CMP[ESI], 0         //check if original has ended
                    JZ Fine
                    DEC ESI
                    XOR ECX, ECX        //set to zero the chars counter
                    JMP Next            //go to Next char
    
            SetRegister :
                PUSH ESI                //after replacement set registers right positions
                    MOV ESI, EDX
                    DEC ESI
                    XOR ECX, ECX
    
            Next :
                LEA EDI, find           //ready to find next char
                    INC ESI
                    CMP[ESI], 0
                    JNE Search
    
            Fine :
                POP EDI
                    MOV[EDI], 0         //close the result string
    

    谢谢大家。

    【讨论】:

    • 当然,对不起,我不经常在论坛上写作,所以......你是对的,我赞成你的回答。什么是“接受你的回答”?现在我想试试你的最后一个编辑版本是否也通过了测试,我是古玩......
    • “接受”是当您单击投票箭头下的复选标记时。像大多数主题一样,如果您想了解更多信息,请在 Google 上搜索“堆栈溢出接受答案”。
    猜你喜欢
    • 2011-04-06
    • 2011-06-06
    • 1970-01-01
    • 2014-12-23
    • 2019-05-14
    • 2013-12-03
    • 2013-07-11
    • 1970-01-01
    • 2017-01-28
    相关资源
    最近更新 更多