I am writing a text editor in ncurses. The program is initialized in raw mode. So I need to manually do many things like deletion, avoiding printing non printable characters, etc.
For deletion:
void console(ch)
{
if(ch == 8) //8 = backspace according to asciitables.com
{
printw("\b");
printw(" ");
}
else
{
addch(ch);
}
}
For avoiding non-printable characters:
void console(ch)
{
bool safe = TRUE;
int avoid[] = { 1,2,3,4,5,6,7,8};
for(int i=0;i<4;i++)
{
while(ch==avoid[i])
{
safe = false;
}
}
if(safe)
{
printw("%c",ch); //Prints the key's characters on the screen.
}
else
{
break;
}
}
In the deletion, I wanted to deleted the previously printed character in the terminal and insert a blank space and move the cursor back to the place of the previous character. But that doesn't work.
In the avoid non-printable character, I wanted to avoid the non printable characters to get printed and only print the printable character. But that also doesn't seems to work.
It would be very helpful if someone points me where I am wrong and correct me. It would be also helpful if anybody can tell me whether there are any specific functions for this in the ncurses library. I am pretty much new to ncurses.
The easiest way in curses to detect "nonprintable" characters is to examine the result from unctrl. If the character is printable, the result is a single-character. Otherwise it is two or more characters:
char *check = unctrl(ch);
int safe = (check != 0 && strlen(check) == 1);
(The manual page goes into some detail).
By the way, addch is more appropriate than printw for printing characters (but keep in mind that its parameter is a chtype, which fits in an int, not char). Again, the manual page would be useful reading to prepare your program.
Related
As part of a larger code repository, I replicated the gets() function by creating my own. The function merely accepts characters into a character array until it encounters a newline character or detects that the string length has been breached. The code is as follows:
void word_enter(char *word_search)
{
char r;
char *m=word_search;
printf("Enter the word to search for: ");
while((r==getchar())!=EOF)
{
if(r=='\n' || m==&word_search[WORD_LEN-1])
{
*m=0;
break;
}
else
{
*(m++)=r;
}
}
}
Apropos WORD_LEN, its value has been defined as 25 by the #define directive.
The function, however, doesn't work properly, and is rather jittery. Inputting a newline does not make the function cease, and the cursor merely shifts to the next line. What explains this? I've been bawling my hearts out figuring out as to what the error is, but to no avail. Be advised that while I'm awfully cognisant of the fact the string.h library has the same function under its belt, I intend to create mine from scratch.
You are comparing r with the return of getchar():
while((r==getchar())!=EOF)
What you should do instead is to assign the return value of getchar() to r:
while((r=getchar())!=EOF)
I'd like some assistance with understand how inputting data in a program of C works. So far I'm used the java syntax having the convenient try{}catch(){}; clause but I don't see it anywhere on C (or I haven't found it?).
Assuming I have the following array;
float f_array[10];
Normally for me to input data I'd either use a scanf(...); or a file which I can read input from, but for the shake of simplicity let's assume I use scanf(...);
And I have the following;
int i;
for(i = 0; i<10; i++){
scanf("%f", &f_array[i]);
}
Now , my question is how to restrain the user from putting in the input a character or a string or the wrong data type for that matter? Also , should I always try to initialize the array before actually putting values in it?
Note that scanf() returns number of elements successfully read, you can check it:
int success = scanf(...);
if (!success) {
scanf("%*[^\n]%*c"):
// OR while(getchar() != '\n');
}
There is, however, a complex solution. You don't use scanf(), but write a custom input method that processes keystrokes and filters out invalid characters, possibly using getch() (Windows/nCurses). Here's a minimized Windows version:
void readFloat(float* in){
int ch, ind = 0;
char buf[100];
while (1){
ch = getch();
if (ch >= '0' && ch <= '9' || ch == '.') {
buf[ind++] = (char)ch;
putchar(ch);
}
else if (ch == 8) /* Backspace */ {
printf("\b \b");
ind --;
}
}
buf[ind] = '\0';
float ret;
sscanf(buf, "%f", &ret);
return ret;
}
So a possible result of the code:
User input (key presses): 123aaa.bbb456
Program filter (displayed on screen): 123.456
Return value: (float)123.456
Now , my question is how to restrain the user from putting in the input a character or a string or the wrong data type for that matter?
Without dedicated hardware support (say, using a keyboard that does not have letter keys, or some device that gives the user an electric shock to discourage them from hitting the 'A' key) there is no way to restrain a user from entering unwanted data.
Instead, you need to write your code with the ASSUMPTION that the user will enter invalid or poorly formed data, and cope with that. It is true that your code is simpler if you can assume an obedient and tractable user who only gives correct input, but the real world isn't like that.
scanf() - reading and interpreting data directly from stdin doesn't actually work well with such an assumption. The return value from scanf() can give you an indication a problem after the fact (e.g. the return value is number of fields successfully input, or EOF). However, when a problem occurs, scanf() handles it in a way you cannot control. Let's say you code has a
scanf("%f", &f_array[i]);
and the user hits the 'X' followed by the Enter key. scanf() will recognise the 'X' character is waiting to be read, and return immediately. The value it returns will not be 1 (which would indicate success). Even worse, the 'X' will be left to be read by a subsequent call of scanf() and the same will happen again (unless a different format is specified). Which means, if you call scanf() in a loop this way, the same will happen over and over again.
Some folks will tell you to simply find a way to read and discard the character 'X'. The problem with that approach is that there are MANY ways for the user to enter bad inputs, and you need to account for all of them. If the user does something you (or your code) doesn't expect, you get problems (e.g. program hanging waiting for the same input repeatedly, input being used as data when it isn't). You're back where you started.
The more robust approach is to simply read a line of input, and do checks before trying to extract a floating point value from it, such as
char buffer[20];
int got_one = 0;
while (!gotone && fgets(buffer, sizeof buffer, stdin) != NULL)
{
if (check_string(buffer))
{
if (sscanf(buffer, "%f", &f_array[i]) == 1)
{
/* yay - we got a floating point value */
got_one = 1;
}
else
{
fprintf(stderr, "Floating point scanning failed. Try again\n");
}
}
else
{
fprintf(stderr, "Bad data discarded. Try again\n");
}
}
Essentially, this provides several hooks so you can check user input in various ways. If you want to, it can be adapted to discard part of a line, and scan useful data from whatever's left.
The key, however, is that the code does not assume the user is well behaved. It only attempts to read a floating point value after a gauntlet of checks, and still copes if the reading fails.
The code can also be adapted to deal with users who enter data that overflows the buffer (e.g. entering 30 floating point characters on a single line). I'll leave that as an exercise.
Also , should I always try to initialize the array before actually putting values in it?
That depends on the needs of your code, but generally speaking I would not bother.
With approaches like I suggest above, you can avoid a circumstance of using the array (or elements of the array) unless valid data has actually been put into it.
All initialising the array will do is obscure cases where the code doing input (reading from the user) has not properly dealt with bad user input.
give the user some feedback on a per input basis.
process each input and allow user to make corrections as you go.
use "atof()" to do the conversion, but it has a couple of quirks:
it tells you there is an error by returning a value of 0.0
it stops processing if/when it finds an invalid char and returns what it has up to that point
eg. 6.35k gives 6.35 -- this usually works out ok;
otherwise you have to check for invalid chars yourself.
try this
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main () {
float f_array[10];
int i;
float input_value;
char temp_string[32];
for(i = 0; i<10; i++){
printf("input a floating point number: ");
scanf("%s",&temp_string[0]);
input_value = atof(temp_string);
while(input_value == 0.0) {
printf("%s is not a valid floating point number\n");
printf("example is 5.6 or 1e32 or 17\n");
printf("try again - input a floating point number: ");
scanf("%s",&temp_string[0]);
input_value = atof(temp_string);
}
f_array[i] = input_value;
printf("String value = %s, Float value = %f\n", temp_string, input_value);
}
/* use the data */
for(i = 0; i<10; i++){
printf("%f\n",f_array[i]);
/* do something */
}
}
I'm going through some C programming questions and I want to make sure I got the fundamentals down. Currently I'm on a word counter question:
Q: Write a function which will determine how many words are in a given string. You can assume that one or more
consecutive white spaces is a delimiter between words, and that the string you pass to your function is null terminated.
I got the thing working, but efficiency is important. I'm wondering how it can be improved. Have to use pointers and no other library besides #include(stdio.h) Thanks!
#include <stdio.h>
int word_counter(char string[])
{
//We start with first word unless we have a empty string then we have no words
int count;
if(*string!='\0'){
count=1;
}
else{
count=0;
return 0;
}
//while we dont reach the end of the string
while(*string!='\0'){
//if we detect a whitespace
if(*string==' '){
//get previous character
string--;
// If previous character is not a space we increase the count
// Otherwise we dont since we already counted a word
if(*string!=' '){
count++;
}
//return pointer to current character
string++;
}
// set pointer to next character
string++;
}
return count;
}
//just to test if it works
int main(void)
{
char str[] = "Hello World!";
printf("How many words? = %i\n", word_counter(str));
return 0;
}
Looking at your code, I see there's a special case for the initial condition of an empty string. Sometimes getting the initial condition out of the way early simplifies the rest of the algorithm, sometimes you can eliminate it by changing how you look at the problem. This time it's the second one.
If you think about this as counting the boundaries between words, the algorithm becomes simpler. There's two ways to define a word boundary, from the front, and from the back.
" Prestidigitation \n"
^ ^
front back
Are we looking for a non-whitespace character after a whitespace character? Or are we looking for a whitespace character after a non-whitespace character?
You also have code that looks backwards in the string (string--), that's often not safe because what if the string starts with whitespace? Then you've walked backwards off the string, so moving backwards should be avoided.
Finally, there's the problem of whether or not there's any whitespace at the end of the string. We'd have to special case the end of the string.
So looking at the first word boundary is the way to go: a non-whitespace character after a whitespace character. Instead of looking backwards, we'll track the state of the previous character (last_was_space below).
That's a non-whitespace character after a whitespace character. What if the string doesn't start with whitespace?
"Basset hounds got long ears."
^
What about this?
Since we have last_was_space, we can initialize it to true and pretend the start of the string starts with whitespace. This also handles leading whitespace like " this is four words".
Finally, there's more types of space than just space like tab and newline and other exotic stuff. Instead of writing if( *space == ' ' || *space == '\n' == ... ) we can use switch to make things tidy and efficient. This is one of those rare cases where you want to take advantage of its "fall through" mechanic to do the same thing for multiple cases.
#include <stdio.h>
// Note that it's `const` since we don't touch the string memory.
int word_counter(const char string[]) {
// Start with no words.
int count = 0;
// Pretend every word starts with space.
short last_was_space = 1;
// Using a for loop to make the movement of the pointer more apparent
for( ; *string!='\0'; string++ ) {
// A switch can be faster than an if/else if.
switch( *string ) {
// There's more than one type of whitespace.
// These are from isspace().
// It takes advantage of switch's fall through.
case ' ':
case '\t':
case '\n':
case '\r':
case '\v':
case '\f':
// Remember we saw a space.
last_was_space = 1;
break;
default:
if( last_was_space ) {
// Non-whitespace after space, count it
count++;
// Remember we didn't see a space.
last_was_space = 0;
}
break;
}
}
return count;
}
Normally I'd use bool from stdbool.h and isspace from ctype.h, but your exercise can only use stdio.h.
I've got an UTF-8 text file containing several signs that i'd like to change by other ones (only those between |( and |) ), but the problem is that some of these signs are not considered as characters but as multi-character signs. (By this i mean they can't be put between '∞' but only like this "∞", so char * ?)
Here is my textfile :
Text : |(abc∞∪v=|)
For example :
∞ should be changed by ¤c
∪ by ¸!
= changed by "
So as some signs(∞ and ∪) are multicharacters, i decided to use fscanf to get all the text word by word. The problem with this method is that I have to put space between each character ... My file should look like this :
Text : |( a b c ∞ ∪ v = |)
fgetc can't be used because characters like ∞ can't be considered as one single character.If i use it I won't be able to strcmp a char with each sign (char * ), i tried to convert my char to char* but strcmp !=0.
Here is my code in C to help you understanding my problem :
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void){
char *carac[]={"∞","=","∪"}; //array with our signs
FILE *flot,*flot3;
flot=fopen("fichierdeTest2.txt","r"); // input text file
flot3=fopen("resultat.txt","w"); //output file
int i=0,j=0;
char a[1024]; //array that will contain each read word.
while(!feof(flot))
{
fscanf(flot,"%s",&a[i]);
if (strstr(&a[i], "|(") != NULL){ // if the word read contains |( then j=1
j=1;
fprintf(flot3,"|(");
}
if (strcmp(&a[i], "|)") == 0)
j=0;
if(j==1) { //it means we are between |( and |) so the conversion can begin
if (strcmp(carac[0], &a[i]) == 0) { fprintf(flot3, "¤c"); }
else if (strcmp(carac[1], &a[i]) == 0) { fprintf(flot3,"\"" ); }
else if (strcmp(carac[2], &a[i]) == 0) { fprintf(flot3, " ¸!"); }
else fprintf(flot3,"%s",&a[i]); // when it's a letter, number or sign that doesn't need to be converted
}
else { // when we are not between |( and |) just copy the word to the output file with a space after it
fprintf(flot3, "%s", &a[i]);
fprintf(flot3, " ");
}
i++;
}
}
Thanks a lot for the future help !
EDIT : Every sign will be changed correctly if i put a space between each them but without ,it won't work, that's what i'm trying to solve.
First of all, get the terminology right. Proper terminology is a bit confusing, but at least other people will understand what you are talking about.
In C, char is the same as byte. However, a character is something abstract like ∞ or ¤ or c. One character may contain a few bytes (that is a few chars). Such characters are called multi-byte ones.
Converting a character to a sequence of bytes (encoding) is not trivial. Different systems do it differently; some use UTF-8, while others may use UTF-16 big-endian, UTF-16 little endian, a 8-bit codepage or any other encoding.
When your C program has something inside quotes, like "∞" - it's a C-string, that is, several bytes terminated by a zero byte. When your code uses strcmp to compare strings, it compares each byte of both strings, to make sure they are equal. So, if your source code and your input file use different encodings, the strings (byte sequences) won't match, even though you will see the same character when examining them!
So, to rule out any encoding mismatches, you might want to use a sequence of bytes instead of a character in your source code. For example, if you know that your input file uses the UTF-8 encoding:
char *carac[]={
"\xe2\x88\x9e", // ∞
"=",
"\xe2\x88\xaa"}; // ∪
Alternatively, make sure the encodings (of your source code and your program's input file) are the same.
Another, less subtle, problem: when comparing strings, you actually have a big string and a small string, and you want to check whether the big string starts with the small string. Here strcmp does the wrong thing! You must use strncmp here instead:
if (strncmp(carac[0], &a[i], strlen(carac[0])) == 0)
{
fprintf(flot3, "\xC2\xA4""c"); // ¤c
}
Another problem (actually, a major bug): the fscanf function reads a word (text delimited by spaces) from the input file. If you only examine the first byte in this word, the other bytes will not be processed. To fix, make a loop over all bytes:
fscanf(flot,"%s",a);
for (i = 0; a[i] != '\0'; )
{
if (strncmp(&a[i], "|(", 2)) // start pattern
{
now_replacing = 1;
i += 2;
continue;
}
if (now_replacing)
{
if (strncmp(&a[i], whatever, strlen(whatever)))
{
fprintf(...);
i += strlen(whatever);
}
}
else
{
fputc(a[i], output);
i += 1; // processed just one char
}
}
You're on the right track, but you need to look at characters differently than strings.
strcmp(carac[0], &a[i])
(Pretending i = 2) As you know this compares the string "∞" with &a[2]. But you forget that &a[2] is the address of the second character of the string, and strcmp works by scanning the entire string until it hits a null terminator. So "∞" actually ends up getting compared with "abc∞∪v=|)" because a is only null terminated at the very end.
What you should do is not use strings, but expand each character (8 bits) to a short (16 bits). And then you can compare them with your UTF-16 characters
if( 8734 = *((short *)&a[i])) { /* character is infinity */ }
The reason for that 8734 is because that's the UTF16 value of infinity.
VERY IMPORTANT NOTE:
Depending if your machine is big-endian or little-endian matters for this case. If 8734 (0x221E) does not work, give 7714 (0x1E22) a try.
Edit Something else I overlooked is you're scanning the entire string at once. "%s: String of characters. This will read subsequent characters until a whitespace is found (whitespace characters are considered to be blank, newline and tab)." (source)
//feof = false.
fscanf(flot,"%s",&a[i]);
//feof = ture.
That means you never actually iterate. You need to go back and rethink your scanning procedure.
I am making a program that is suppose to check that a certain input-string only contains a certain type of characters (ASCII-characters). I have a string of characters that belong to the ASCII-standard, and for each of the characters in my input-string, I check (character by character) if it exist in the ASCII-string. If the character do exist, I will move them to a new string, an output string. If they do not exist, I inform the user and exit the program.
However, the input-string I am making could both be created from the command line, but also from a text-file. My textfiles always seem to contain several new-lines, and they always end with a new-line ("\n"-character) which always makes my program to report errors, either in the end of the string or in the middle.
How do I rearrange the code of the below program in order for it to recognize the "\n"-characters? Below is a simplification of the code, not showing how I define the inputstring nor the string with the ASCII-characters.
char inputstring[];
char outputstring[];
char asciicharacters[];
* Here I read a text-file to the inputstring, and the ASCII-characters to the asciicharacters-string *
int k,i=0;
for(i=0;i<strlen(inputstring);i++)
{
for(k=0;k<strlen(asciicharacters);k++)
{
if(inputstring[i] == asciicharacters[k])
{
outputstring[i] = inputstring[i];
break;
}
else if(k == strlen(asciicharacters)-1)
{
printf("The character: %c, is not ASCII-standard",input[i]);
exit(1);
}
}
}
How do I create another condition that makes the program skip (and accept as an ASCII-standard) when I reach a "\n"-character?
You can add this else if before the final else if.
else if( inputstring[i] == '\n' )
{
break;
}