Here is my eiffel program, which is basically doing space removal (remove the redundant spaces in a given text file) to follow the regular expresson: A+(SA+)*EOL, where for each line in the file, it must start with alphabets and only one spaces between alphabets.
My question is, base on this program, how can I extend it to make every other word to be uppercase? i.e. 2th, 4th, 6th, etc.
feature {NONE} -- Main routine
copy_file
-- Copy a file character by character from input to output
require
input_open: input.is_readable
output_open: output.is_writable
local flag: INTEGER
has_read_space: BOOLEAN
empty_line : BOOLEAN
do
empty_line: True
flag := 0 -- 0 for previous space, 1 for previous char
from read_char -- Must prime the pump by reading the first character
until ch = EOF
loop
from
ch := input.last_character
until
ch = EOL
loop
if ch = Space_char and flag = 0 then -- leading spaces
read_char
elseif ch /= Space_char and flag = 0 then -- see first charater after space
if has_read_space then -- this clause make sure the space will only place in between two words instead of end of lin
output.putchar (Space_char)
end
output.putchar (ch)
empty_line := False
flag := 1
read_char
elseif ch = Space_char and flag = 1 then -- see space after characters
has_read_space := True -- Don't output it right away
flag := 0
read_char
elseif ch /= Space_char and flag = 1 then -- see character after character
output.putchar (ch)
read_char
end
end
if empty_line = False then
output.putchar (EOL) -- if line is not empty, then we place EOL at the end of line
end
flag := 0 -- reset it to 0 to make sure the next follow the same routine
has_read_space := False -- reset it to avoid placing space in the beginning of line
empty_line := True -- reset to proceed to new line
read_char
end
-- At end of file, nothing to do in Eiffel except close the files
input.close
output.close
end
Just figured it out.
Simply set another flag, say evenWord, initialize it to be false.
and in the elseif ch = Space_char and flag = 1 clause flip the flag evenWord := not evenWord It works
Related
I have to make a program that reads some "random" strings of letters and numbers from a text file and checking if they meet some conditions that makes them a valid password.
The conditions are: -Have exactly 4 digits
-Have exactly 8 characters
-Have at least one uppercase letter and at least one lowercase letter
The program reads the file and it outputs the number of valid passwords.
This is the format of the text file:
"eR68G12a 91jY643ebjp eRty74kLh 24fG92 aj85gt32 dGb9357jKoup2 " (on a single line)
The code:
´´´
Program Ej23_version3;
var
char1,char2:char;
mayus,minus:boolean; // mayus and minus would be uppercase and lowercase respectively
cantDigitos,cantCaracteres,contrasenasValidas:integer;
datos:text;
Begin
assign(datos,'Datos_guia3_ej23.txt'); reset(datos);
contrasenasValidas := 0;
char1 := ' ';
Read(datos,char2);
while not eof(datos) do
Begin
mayus := false; minus := false; cantDigitos := 0; cantCaracteres := 0;
if (char1 = ' ') and (char2 <> ' ') then //check if its the beggining of the word
Begin
while not eof(datos) and (char2 <> ' ') do
Begin
cantCaracteres := cantCaracteres + 1;
if char2 = UPCASE(char2) then // if the character2 is equal to the uppercase version of the character2, character2 is uppercase
mayus := true
else
if (char2 in ['0'..'9']) then
cantDigitos := cantDigitos + 1
else
minus := true;
if eof(datos) then // when it reaches the end of the file, it also reads and checks the last character
if char2 = UPCASE(char2) then
mayus := true
else
if (char2 in ['0'..'9']) then
cantDigitos := cantDigitos + 1
else
minus := true;
End;
if minus and mayus and (cantDigitos = 4) and (cantCaracteres = 8) then //if all conditions are met, the password is valid and its added to the counter
contrasenasValidas := contrasenasValidas + 1;
char1 := char2; Read(datos, char2); //char2 should be an empty character by this point, so it passes that value to char1 and reads the next character
End
End;
WriteLn(contrasenasValidas);
End.
But when i run it, it just gets stuck there with only the prompt ticking
The problem is in the way you read the file (character by character).
It would be better to read it all at once, and examine all the sequences of eight characters one by one.
uses
SysUtils;
var
LFile: TextFile;
LStr, LSubStr: string;
LStartIndex: integer;
LExit: boolean;
begin
AssignFile(LFile, 'Datos_guia3_ej23.txt');
Reset(LFile);
ReadLn(LFile, LStr); // Get the whole line
LStartIndex := 1; // Search all 8 characters sequences, starting from the first character
LExit := FALSE;
repeat
LSubStr := Copy(LStr, LStartIndex, 8);
if Length(LSubStr) = 8 then
begin
// Here check other conditions
// ...
Inc(LStartIndex);
end else
LExit := TRUE;
until LExit;
CloseFile(LFile);
end.
So my task consists in a password with 3 different characters from the previous password in PL/SQL.
Here is the code:
IF old_password IS NOT NULL THEN
differ := length(old_password) - length(password);
differ := abs(differ);
IF length(password) < length(old_password) THEN
m := length(password);
ELSE
m := length(old_password);
END IF;
FOR i IN 1..m LOOP
IF substr(password,i,1) != substr(old_password,i,1) THEN
differ := differ + 1;
END IF;
END LOOP;
IF differ < 3 THEN
raise_application_error(-20011, 'Password should differ from the \
old password by at least 3 characters');
END IF;
END IF;
-- Everything is fine; return TRUE ;
RETURN(TRUE);
If my old password is "aaa" and I change it for "aaa222", it gives me an error. That's fine.
But if my old password is "aaa" and I change it for "aaa2222", it doesn't give me an error I don't know why. This should give me an error..
What's wrong? Any solutions?
From the code you have posted your results do not make sense, I think you have a typo in your posted code (see my comments below) , but lets analyse what the code is actually doing.
obvious
IF old_password IS NOT NULL THEN
These two lines in your first case set the difference to 3 and the second case to 4 as the strings are different lengths
differ := length(old_password) - length(password);
differ := abs(differ);
This block in either case selects the shorter of the two passwords so will in both cases return 3 (inital password of aaa)
IF length(password) < length(old_password) THEN
m := length(password);
ELSE
m := length(old_password);
END IF;
You loop through the length of the short password and compare it character by character with the longer password in both the first three characters are aaa so match so you will not get into the if statment
FOR i IN 1..m LOOP
IF substr(password,i,1) != substr(old_password,i,1) THEN
differ := differ + 1;
END IF;
END LOOP;
In you first case differ will be 3 and in your second case 4 so this will not evaluate to true unless you have <= rather than < (this is where I think you have a typo in the code you have posted)
IF differ < 3 THEN
raise_application_error(-20011, 'Password should differ from the \
old password by at least 3 characters');
END IF;
END IF;
Return true in both cases.
-- Everything is fine; return TRUE ;
RETURN(TRUE);
I have a procedure, that in theory, should be skipping whitespace using a look_ahead loop. Problem is, it's not working, if there's any whitespace in the input file, it is adding it to the array of records. I think my logic is correct, but could use another pair of eyes to let me know what I'm missing, and why it's not working.
PROCEDURE Read(Calc: OUT Calculation) IS
EOL: Boolean;
C: Character;
I: Integer := 1;
BEGIN
LOOP
LOOP
Look_Ahead(C, EOL);
EXIT WHEN EOL or C /= ' ';
Get(C);
END LOOP;
EXIT WHEN ADA.Text_IO.END_OF_FILE;
Look_Ahead(C, EOL);
IF Is_Digit(C) THEN
Calc.Element(I).Kind := Number;
Get(Calc.Element(I).Int_Value);
ELSE
Calc.Element(I).Kind := Symbol;
Get(Calc.Element(I).Char_Value);
END IF;
Calc.Len := Calc.Len+1;
IF Calc.Element(I).Char_Value = '=' THEN
EXIT;
END IF;
I := I+1;
END LOOP;
END Read;
EDIT: If any of the other procedures, the code for the record etc is needed for an answer, let me know and I will post it.
For Ada.Text_IO.Look_Ahead, ARM A.10.7(8) says
Sets End_Of_Line to True if at end of line, including if at end of page or at end of file; in each of these cases the value of Item is not specified. Otherwise, End_Of_Line is set to False and Item is set to the next character (without consuming it) from the file.
(my emphasis) and I think the "without consuming it" is key. Once Look_Ahead has found an interesting character, you need to call Get to retrieve that character.
I hacked this little demo together: I left end-of-file to exception handling, and I called Skip_Line once end-of-line’s been seen because just Get wasn’t right (sorry not to be more precise!).
with Ada.Text_IO;
with Ada.IO_Exceptions;
procedure Justiciar is
procedure Read is
Eol: Boolean;
C: Character;
begin
-- Instead of useful processing, echo the input to the output
-- replacing spaces with periods.
Outer:
loop
Inner:
loop
Ada.Text_IO.Look_Ahead (C, Eol);
exit Outer when Eol; -- C is undefined
exit Inner when C /= ' ';
Ada.Text_IO.Get (C); -- consume the space
Ada.Text_IO.Put ('.'); -- instead of the space for visibility
end loop Inner;
Ada.Text_IO.Get (C); -- consume the character which isnt a space
Ada.Text_IO.Put (C); -- print it (or other processing!)
end loop Outer;
Ada.Text_IO.Skip_Line; -- consume the newline
Ada.Text_IO.New_Line; -- clear for next call
end Read;
begin
loop
Ada.Text_IO.Put ("reading: ");
Read;
end loop;
exception
when Ada.IO_Exceptions.End_Error =>
null;
end Justiciar;
Usually it's better to read an entire line and parse it than to try to parse character by character. The latter is usually more complex, harder to understand, and more error prone. So I'd suggest something like
function De_Space (Source : String) return String is
Line : Unbounded_String := To_Unbounded_String (Source);
begin -- De_Space
Remove : for I in reverse 1 .. Length (Line) loop
if Element (Line, I) = ' ' then
Delete (Source => Line, From => I, Through => I);
end if;
end loop Remove;
return To_String (Line);
end De_Space;
Line : constant String := De_Space (Get_Line);
You can then loop over Line'range and parse it. Since I'm not clear if
Get(C);
Get(Calc.Element(I).Int_Value);
Get(Calc.Element(I).Char_Value);
represent 1, 2, or 3 different procedures, I can't really help with that part.
What is the correct way to convert an always changing integer variable to a string (to be displayed on a VGA monitor)? I have a series of if statements that take care of padding (so that the resulting string is always a certain length but as soon as I change:
resulting_string <= integer'image(87465);
to:
resulting_string <= integer'image(some_int_var);
I get an "Expression is not constant" error. What is the correct way to convert an always changing integer variable (that could be any int within the integer limits) to a string?
edit: not duplicate of the other question
'image (..) does not work on signals.
I think you are still missing the main problem: A monitor can not display strings or chars!
You need to implement
1. a graphic buffer,
2. a buffer reader that outputs VGA data.
Then you need to implement a manipulator to
draw shapes
clear the screen
move areas and
copy pictures into the buffer.
The glyphs are stored in a ROM and are choosen by a BCD value for each digit.
Converting an integer to a 'string' does not need a ASCII string, because the result is a BCD code (not ASCII). So you need to implement an (un)signed to BCD converter, too.
I went at it from every direction and finally found that I had to make a giant case block to get it to work. Now I can finally display rapidly changing variables that are really helpful for debugging. It's unfortunate that the solution had to be so retarded though..
(I already have a ROM for displaying text that the resulting string is sent to.)
function int_to_str(int : integer) return string is
variable a : natural := 0;
variable r : string(1 to 11);
begin
a := abs (int);
case a is
when 0 => r := "0 ";
when 1 => r := "1 ";
when 2 => r := "2 ";
when 3 => r := "3 ";
.
.
.
when 1000 => r := "1000 ";
when others => r := "???????????";
end case;
if (int < 0) then
r := '-' & r(1 to 10);
end if;
return r;
end int_to_str;
I have to write a program in Pascal which has to detect how many words on a text (input by the user) start with a certain letter. I can't use arrays, can you give me any hints as to where to start?
If you know which letter, you merely need to keep a counter, no need for arrays.
If you don't know which letter, keep 26 counters. Stupid, but works as per your spec.
First thing to do is define the set of characters that constitute letters, or conversely which ones constitute non-letters.
Write a function that takes a character and returns a boolean based on whether that character is a letter. Then loop through the string and call it for each character. When you detect a letter right after a non-letter or at the start of the string, increment your counter if it is the target letter.
count instances of SPACE LETTER plus first word if it matches.
(S) is your input string;
Create a for loop that goes from 1 to the length of (S) - 1.
Inside loop, check is (S)[i] = ' ' and (S)[i+1] = 't' where i is the loop counter and 't' is the letter starting the word you want to count
If criteria in step two matches then increment a counter.
Note the minus one on the loop size.
Also, remember that the very first letter of the string may be the one you want to match and that will not get picked up by the loop defined above.
If you need to make your code smarter in that it can locate a specific letter rather than a hardcoded 't' then you can pass the requested character as a parameter to the function/procedure that your loop is in.
Off the top of my head - not tested
function WordCount(const S: string; const C: Char): Integer;
const
ValidChars: Set of Char [A..Z, a..z]; // Alter for appropriate language
var
i : Integer;
t : string;
begin
Result := 0;
if Length(S) <> 0 then
begin
t := Trim(S); // lose and leading and trailing spaces
t := t + ' '; // make sure a space is the last char
repeat
if (t[1] in ValidChars) and (t[1] = C then
inc(Result);
i := Pos(' ', t);
t := Copy(t(i+1, Length(t));
until Length(t) = 0;
end;
end;
Why would you need an array or a case statement?