Перевести в верхний регистр первую букву каждого слова
Есть некая строка, например: "Assembler language programming is the fastest in the world". Задача сделать каждую букву нового слова заглавной. Есть код-макет для немного другого примера(там просто удалить первое слово) и не могу понять как реализовать, у меня была идея с циклом, но я не придумал как выделять слова, т.к. количество символов в каждом слове разная. Может у вас есть какие-то идеи?
Код-макет:
.model small
.stack 100h
.data
Original_string db 'Assembler language programming is the fastest in the world.', 13, 10, '$'
Formed_line db 255 DUP(?)
.code
.386
main:
mov ax, @data
mov ds,ax
mov es,ax
; ========= Вывод начальной строки. ============
mov dx, offset Original_string
mov ah, 9
int 21h
; ===============================
cld
mov ecx, LENGTHOF Original_string
sub ecx, 10
mov esi, [OFFSET Original_string]+10 ; Зададим адресс источника + длина строки 'Assembler'
mov edi, OFFSET Formed_line ; Зададим адрес получателя данных
rep movsb ; Сформируем нужную строку
; ====== Вывод сформированной строки. ============
mov dx, offset Formed_line
mov ah, 9
int 21h
; ===============================
mov ax, 4C00h ; Номер функции DOS:4C00h закончить программу в АХ
int 21h ; Вызов функции DOS из АХ
end main
Ответы (2 шт):
Если заглянем в таблицу символов ASCII, то увидим там две группы букв: 'A'..'Z' и 'a'..'z' - они следуют по возрастанию их кодов именно в таком порядке. Любой другой символ вне этих групп можем считать символом-разделителем слов. Соответственно, букву можем считать первой в слове, если предыдущий символ является разделителем. Для этого нужна переменная, в которой будем сохранять статус очередного выводимого символа: 0 - если разделитель, 1 - если буква. И при считывании очередной маленькой буквы будем проверять эту переменную: если равна 0, значит буква первая в слове - превращаем её в большую, просто добавив 'A'-'a'.
В общем, приведу вариант цикла преобразования без проверки переполнения буфера (т.е., копирование будет происходить до тех пор, пока не встретится символ '$'). Указатель на исходную строку - DS:SI, указатель на буфер с результирующей строкой - ES:DI (может совпадать и исходной строкой). Код не отлаживал, так что могут быть косяки))
mov ah,'$' ;в AH - символ конца строки
mov cx,1 ;в CX - просто 1
xor dx,dx ;предыдущий символ: 0 - разделитель, 1 - буква
jmp LoadChar ;переходим к циклу
;записываем разделитель
StoreDelim:
xor dx,dx ;помечаем как разделитель
stosb ;записываем в новую строку
jmp LoadChar ;переходим к следующему циклу
;записываем букву
StoreLetter:
mov dx,cx ;помечаем как букву
StoreChar:
stosb ;записываем в новую строку
;(переходим к следующему циклу)
LoadChar:
lodsb ;считываем очередной символ
cmp al,ah ;конец строки?
je StoreEOL ;да: выход из цикла
;далее проверяем символ, и прыгаем на
;соответствующую ветку для буквы или разделителя
cmp al,'A'
jb StoreDelim ;если меньше 'A', то записываем разделитель
cmp al,'Z'
jbe StoreLetter ;если ,больше или равно 'A' и меньше или равно 'Z', то то записываем букву (большая)
cmp al,'a'
jb StoreDelim ;если меньше 'a', то записываем разделитель
cmp al,'z'
ja StoreDelim ;если больше 'z', то записываем разделитель...
;...иначе: очередной символ - маленькая буква
test dx,dx ;предыдущий символ - разделитель?
jnz StoreChar ;нет: выводим букву как есть
add al,'A'-'a' ;да: преобразуем букву в большую...
jmp StoreLetter ;...и выводим её (с пометкой в DX)
;записываем символ конца строки и выходим из цикла
StoreEOL:
stosb
;.............
;код дальше...
В таблице ASCII, разница между заглавной и прописной буквой ровно 20h. Тогда получается, что отличаются они всего одним битом(6), т.к. 0x20=00100000b. Если прочитать символ и взвести OR в нём указанный бит(6), то получим прописную букву, а если сбросить AND - заглавную. Допустим "маркером окончания строки" является символ точки(.), соответственно код будет выглядеть примерно так (fasm x32):
format pe gui
include 'win32ax.inc'
;//----------
.code
start: invoke MessageBox,0,szStr,0,0 ;// покажем исходную строку
mov esi,szStr ;// указатель на неё
mov ah,11011111b ;// маска = 20h
and byte[esi],ah ;// делаем первый символ строки заглавным
@@: lodsb ;// берём в AL очередной..
cmp al,'.' ;// конец строки?
je @stop ;// да - на выход.
cmp al,' ' ;// это пробел? (новое слово)
jne @begin ;// нет - пропустить.
and byte[esi],ah ;// иначе: сбросить бит(6) - заглавная буква.
@begin: jmp @b ;// промотать цикл, пока не встретим точку(.)
@stop: invoke MessageBox,0,szStr,0,0 ;// покажем результат
invoke ExitProcess,0
.end start
;//----------
.data
szStr db 'Assembler language programming is the fastest in the world.',0