Is it possible for me to remove this last if statement? - c

I've written a small utility to open up executables and spit out certain printable strings it finds.
It works fine but I was wondering, is there some way I could remove one of these if statements? I was trying to see how I could arrange my conditionals so I wouldn't need the 3 different if statements but I don't see how I can do it with the current structure.
#include <stdio.h>
#define MAX_STR_SIZE 0x666
#define MIN_STR_SIZE 0x5
int main(int argc, char** argv)
{
int ch;
int pos = 0;
FILE* f;
char buff[MAX_STR_SIZE];
if (argc>1 && (f=fopen(argv[1], "rb")))
{
while ((ch=getc(f)) != EOF)
{
if (ch >= ' ' && ch <= 'z') // is printable char?
{
buff[pos++] = ch;
buff[pos] = '\0';
if (pos == (MAX_STR_SIZE-1)) // is current string > max length?
{
printf("%08x: %s\n", ftell(f), &buff[0]);
pos = 0;
}
}
else // non-printable char, print any string in buffer and start over
{
if (pos > (MIN_STR_SIZE - 1)) // is current string > min string?
{
printf("%08x: %s\n", ftell(f), &buff[0]); // print current string
}
pos = 0;
}
}
if (pos > (MIN_STR_SIZE - 1)) // any remaining string left to print?
{
printf("%08x: %s\n", ftell(f), &buff[0]);
}
fclose(f);
}
}

I believe this version eliminates most of the if statements (or at least collapses them together:
#include <stdio.h>
#define MAX_STR_SIZE 0x666
#define MIN_STR_SIZE 0x5
int main(int argc, char** argv)
{
int ch;
int pos = 0;
FILE* f;
char buff[MAX_STR_SIZE];
if (argc>1 && (f=fopen(argv[1], "rb")))
{
while ((ch = getc(f)) != EOF)
{
pos = 0;
while (ch >= ' ' && ch <= 'z' && pos < (MAX_STR_SIZE-1)) {
buff[pos++] = ch;
ch = getc(f);
}
if (pos > (MIN_STR_SIZE - 1)) // is current string > min string?
{
buff[pos] = '\0';
printf("%08x: %s\n", ftell(f), buff);
}
}
fclose(f);
}
}

The last if seems to be necessary for the current logic of the code.
However, the check in your code is not entirely correct. Why don't you use isprint() function to check whether a character is printable or not? Something like this:
if (isprint(c)) // is printable char?
{
//c is printable
}

Related

My program can't output the lines correctly

I'm relatively new in C and I currently reading Kernighan's book.
One of the problems in the book is to create an algorithm that from a input line output the line if it is more than 10 characters long.
The point is I'm frustrated because I cant find what is wrong with my code. I debugged and recreate it many times but still cant find out what's going on!
The escape character from function getl() is '.' (dot), and sometimes works and other times don't. If you compile it and test you will see:
gcc -Wall -o out 'script.c'
The question header from the book is:
“Exercise 1-17. Write a program to print all input lines that are longer than 10 characters.”
I'm sure that's relatively easy, but I really wanted to know why this algorithm is not working as expected, i think it has something to do with '\n'.
If someone could help me find out what's the problem with the code, I would appreciate it.
Code
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c;
int count;
for (count = 0 ; count < lim-1 && (c = getchar()) != '.' ; count++) {
if (c == '\n') {
line[count] = '\n';
count++;
break;
}
line[count] = c;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
getl(line, MAX);
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
Your code almost works. You just seem to have some repeated lines here and there that confuse things.
Specifically, you are calling getl(line, MAX); twice in a row. The first gets the input, but don't save the count, the second has only an empty stdin buffer to work with so no sensible count is saved from that. Removing the first call that don't save the count fixes your issue.
#include <stdio.h>
#define MAX 10000
int getl(char line[], int lim) {
char c = getchar();
int count;
for (count = 0 ; c != '.' ; count++) {
line[count] = c;
c = getchar();
}
line[count++] = '\n';
return count;
}
int main() {
char line[MAX];
int len = 1;
for (; len > 0 ;) {
len = getl(line, MAX);
if (len >= 10)
printf("%s", line);
}
return 0;
}
First, you're calling your getl function twice instead of once (you only want to read lines one by one). Fixing that should work.
Then I think you shouldn't add the trailing '\n' to your lines, just print it when your line is longer than 10 characters, in your code, the '\n' will be counted as a character.
Here's the modified code:
#include <stdlib.h>
#include <stdio.h>
#define MAX 10000
int getl(char line[])
{
char c;
int count;
for (count = 0; count < MAX - 1 && (c = getchar()) != '.' ; count++)
{
if (c == '\n')
break;
line[count] = c;
}
line[count] = '\0';
return (count);
}
int main()
{
char line[MAX];
int len = 1;
while (len > 0)
{
len = getl(line);
if (len >= 10)
printf("%s, c = %i\n", line, len);
}
return (0);
}
This should work. https://ideone.com/cXXRUH
#include <stdio.h>
#define MAX 10000
int getline_length(char line[]) {
char ch;
int count = 0;
printf("\nWaiting for INPUT...");
// Using clear while loop to get input, removing redundent complexity
// Either `.` or `\n` consider End Of Line
while(count < MAX-1 && ((ch = getchar()) != '.' || (ch = getchar()) != '\n')) {
line[count++]=ch;
}
line[count] = '\0';
return count;
}
int main() {
char line[MAX];
while(1) {
// reset array before each input
memset(line, 0, sizeof(line));
int len = getline_length(line); //No need to pass limit
if (len >= 10) {
printf("%s", line);
} else {
printf("len < 10");
}
}
return 0;
}

Type checking arbitrary length array in ANSI C

Hi I am confined to stdio.h, stdlib.h and string.h and I need to ask a user for input - the input can be any number of characters between 1 and 6, however the first two characters MUST be an uppercase alphabetical letter, and the remaining four characters MUST be a number between 0 and 9.
Examples of valid input:
AB1
AB1234
AB
A
Examples of Invalid Input:
AB12345 (too many characters)
123 (first two characters are not uppercase letters)
ABA (a character after the second one is not a numeric value)
Here is my attempt so far (just bear in mind I have almost no experience with C, the likelihood that this solution is "idiomatic" is next to none, and the reason I am asking this is so that I can learn):
Flightcode is a char array defined as flightcode[7] it lives inside another struct called flight. I am fgetsing it into a temp_array[7] first and then strcpying it into the flight->flightcode such that the null terminator is appended and I don't know a better way of doing that.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
char * scanline(char *dest, int dest_len);
int main(){
char temp_string[100];
flight_t flight[MAX_NUM_FLIGHTS + 1];
int correct_code = 0;
printf("Enter flight code>\n");
scanline(temp_string, sizeof(flight->flightcode));
strcpy(flight->flightcode, temp_string);
while(correct_code == 0)
{
for(int i = 0; flight->flightcode[i] != '\0' && correct_code == 0; i++)
{
while((i < 2 && (flight->flightcode[i] <= 64 || flight->flightcode[i] >= 91)) || (i > 1 && (flight->flightcode[i] < 48 || flight->flightcode[i] >= 58)))
{
printf("Invalid input.\n");
scanline(temp_string, sizeof(flight->flightcode));
strcpy(flight->flightcode, temp_string);
}
if((i < 2 && (flight->flightcode[i] > 64 || flight->flightcode[i] < 91)) || (i > 1 && (flight->flightcode[i] >= 48 || flight->flightcode[i] < 58)))
{
correct_code = 1;
}
}
}
}
char * scanline(char *dest, int dest_len){
int i, ch;
i = 0;
for (ch = getchar();
ch != '\n' && ch != EOF && i < dest_len -1; ch = getchar())
dest[i++] = ch;
dest[i] = '\0';
while (ch != '\n' && ch != EOF)
ch = getchar();
return (dest);
}
Scansets and the %n specifier could be used to parse the input.
The format string "%n%2[A-Z]%n%4[0-9]%n" uses the %n specifier in three places to capture the number of characters processed. The scanset %2[A-Z] will scan up to two characters if the characters are in the set of upper case letters. %4[0-9] will scan up to four characters if the characters are digits.
If two values are scanned by sscanf, the number of characters processed are subtracted to make sure there are two leading upper case characters and six or fewer total character and the trailing character is the terminating zero.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
char * scanline(char *dest, int dest_len);
int main(){
int head = 0, leading = 0, tail = 0;
int correct_code = 0;
int result = 0;
char temp_string[100];
char upper[3] = "";
char digits[5] = "";
flight_t flight[MAX_NUM_FLIGHTS + 1];
do {
printf("Enter flight code>\n");
scanline(temp_string, sizeof(temp_string));
if ( 0 < ( result = sscanf ( temp_string, "%n%2[A-Z]%n%4[0-9]%n", &head, upper, &leading, digits, &tail))) {
if ( 1 == result && 0 == temp_string[leading]) {
correct_code = 1;
break;
}
if ( 2 == result && 2 == leading - head && 7 > tail - head && 0 == temp_string[tail]) {
correct_code = 1;
}
else {
printf ( "invalid input\n");
}
}
else {
printf ( "invalid input\n");
}
} while(correct_code == 0);
printf ( "Input is: %s\n", temp_string);
strcpy(flight->flightcode, temp_string);
return 0;
}
char * scanline(char *dest, int dest_len){
int i, ch;
i = 0;
for (ch = getchar(); ch != '\n' && ch != EOF && i < dest_len -1; ch = getchar()) {
dest[i++] = ch;
}
dest[i] = '\0';
while (ch != '\n' && ch != EOF) {
ch = getchar();
}
return dest;
}
Your function scanline does not do much more than the standard function fgets. I propose to use the standard function instead. Removing the trailing newline '\n' is easy.
I have split the checks into 3 parts:
Check the length to be more than 0 and not more than MAX_FLIGHTCODE_LEN.
Check the first 2 characters to be uppercase letters A..Z
Check the remaining characters to be digits 0..9
Proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FLIGHTCODE_LEN 6
#define MAX_CITYCODE_LEN 3
#define MAX_NUM_FLIGHTS 50
#define DB_NAME "database"
typedef struct {
int month;
int day;
int hour;
int minute;
} date_time_t;
typedef struct {
char flightcode[MAX_FLIGHTCODE_LEN + 1];
date_time_t departure_dt;
char arrival_city[MAX_CITYCODE_LEN + 1];
date_time_t arrival_dt;
} flight_t;
date_time_t departure_dt;
date_time_t arrival_dt;
int main(void){
char temp_string[100];
flight_t flight[MAX_NUM_FLIGHTS + 1];
int correct_code;
size_t len;
int i;
do
{
/* we first assume the code is correct and set this to 0 on any error */
correct_code = 1;
printf("Enter flight code>\n");
if(fgets(temp_string, sizeof(temp_string), stdin) == NULL)
{
if(feof(stdin)) fprintf(stderr, "no input (EOF)\n");
else perror("fgets");
correct_code = 0;
temp_string[0] = '\0';
}
if(correct_code)
{
len = strlen(temp_string);
/* cut off newline
* Use a loop to handle CR and LF just in case Windows might leave more than one character */
while((len > 0) &&
((temp_string[len - 1] == '\n') ||
(temp_string[len - 1] == '\r')))
{
len--;
temp_string[len] == '\0';
}
if(len > MAX_FLIGHTCODE_LEN)
{
correct_code = 0;
fprintf(stderr, "Input must not be longer than %d characters.\n", MAX_FLIGHTCODE_LEN);
}
if(len == 0)
{
correct_code = 0;
fprintf(stderr, "Empty input.\n");
}
}
/* check first two letters */
for(i = 0; (i < 2) && (i < len) && correct_code; i++)
{
/* you could use function isupper when you make sure the locale is set to "C" */
if((temp_string[i] < 'A') || (temp_string[i] > 'Z'))
{
correct_code = 0;
fprintf(stderr, "first two characters must be uppercase letters. Found '%c' at position %d\n", temp_string[i], i);
}
}
/* check digits starting from 3rd character */
for(i = 2; (i < MAX_FLIGHTCODE_LEN) && (i < len) && correct_code; i++)
{
/* you could use function isdigit here */
if((temp_string[i] < '0') || (temp_string[i] > '9'))
{
correct_code = 0;
fprintf(stderr, "Third to last characters must be digits. Found '%c' at position %d\n", temp_string[i], i);
}
}
if(correct_code)
{
/* we already checked that length is not more than MAX_FLIGHTCODE_LEN, so we don't need strncpy to avoid buffer overflow */
strcpy(flight->flightcode, temp_string);
printf("Valid code: %s\n", flight->flightcode);
}
else
{
fprintf(stderr, "Invalid code.\n");
}
} while(!correct_code);
return 0;
}
You have a requirement that does not fit well with what scanf can easily do, so I would stay away from it, and use fgets as a primary read utility.
But as the number of acceptable uppercase and digit characters is not fixed by only limited I would use a custom parser based on a state machine. It is probably not the most elegant nor efficient way but it is simple, robust and easy to maintain.
Just to demonstrate it, I have allowed blank characters before the first uppercase one and spaces after the last digit. So the following code accept an arbitrary long line following this regex pattern [ \t]*[A-Z]{1,maxupper}[0-9]{0,maxdigit}\s* provided it receives a buffer of size at least maxupper+maxupper+1. It returns a pointer to the buffer is successful or NULL if not.
As you have said that you could not use the ctype macros, I have defined ASCII (or any charset derived from ASCII) equivalent for the ones I have used.
#define TRUE 1
#define FALSE 0
inline int isupper(int c) {
return c >= 'A' && c <= 'Z'; // only for ASCII and derived
}
inline int isdigit(char c) {
return c >= '0' && c <= '9'; // guarantee per standard
}
inline int isblank(int c) {
return c == ' ' || c == '\t';
}
inline int isspace(int c) {
static const char spaces[] = " \t\r\n\v";
for(const char *s=spaces; *s != '\0'; s++) {
if (c == *s) return TRUE;
}
return FALSE;
}
char *get_string(char *buffer, int maxupper, int maxdigit, FILE *fd) {
char buf[16]; // any size >=2 will fit
char *cur = buffer;
int state = 0, uppersize=0, digitsize=0;
for (;;) { // allow lines longer than buf
if (NULL == fgets(buf, sizeof(buf), fd)) {
*cur = '\0'; // EOF: do not forget the terminating NULL
return state >= 1 ? buffer : NULL; // must have at least 1 char
}
for (char *b=buf; *b!='\0'; b++) {
switch(state) {
case 0: // spaces before first uppercase
if (isblank(*b)) break;
state++;
case 1: // first uppercase
if (! isupper(*b)) {
state = 5; // must read up to \n
break;
}
state++;
case 2: // process uppercase chars
if (! isupper(*b)) {
if (uppersize > 0) state++;
else {
state = 5; // must read up to \n
break;
}
}
else {
if (uppersize >= maxupper) {
state = 5; // must read up to \n
break;
}
*cur++ = *b;
uppersize++;
break;
}
case 3: // process digit chars
if (! isdigit(*b)) {
state++;
}
else {
if (digitsize >= maxdigit) {
state = 5; // must read up to \n
break;
}
*cur++ = *b;
digitsize++;
break;
}
case 4: // allow spaces after last digit
if ('\n' == *b) {
*cur = '\0';
return buffer;
}
if (! isspace(*b)) state++
break;
case 5: // on error clean end of line
if ('\n' == *b) return NULL;
}
}
}
}
Then in your code, you simply calls it that way:
...
printf("Enter flight code>\n");
if (NULL == get_string(flight->flightcode, 2, 4, stdin)) {
// process the error
...
}
...
First thing, realize that your question text is missing a question. Moreover, your question title makes no sense.
Anyway, here it is a possible, purposely very ugly, solution. Approach: you want to do X, so you write the code to do X. Let's start with scanline():
int scanline(char *dest, int dest_len)
{
int i = 0;
int ch;
while (1) {
// Read
ch = fgetc(stdin);
// Check
if (ch == EOF)
break;
if (ch == '\n')
break;
if (i >= dest_len - 1)
break;
// Use
dest[i] = ch;
++i;
}
dest[i] = 0;
// Is the string finished? Ok!
if (ch == '\n' || ch == EOF)
return 1;
// Otherwise discard the rest of the line. Not ok!
while (ch != '\n' && ch != EOF)
ch = fgetc(stdin);
return 0;
}
I know this is ugly, but I believe that it is helpful to clarify the three steps involved in file input: read, check, use. Note that it returns true if the line was up to the required number of characters (one less than the buffer size to accomodate for the terminator.
Then you want to check if:
scanline() is successful
there is at least one character.
character 0 is between 'A' and 'Z'
character 1 is between 'A' and 'Z'
character 2 is between '0' and '1'
character 3 is between '0' and '1'
character 4 is between '0' and '1'
character 5 is between '0' and '1'
Lets write the code for that:
int main(void)
{
flight_t flight;
while (1) {
printf("Enter flight code>\n");
if (!scanline(flight.flightcode, sizeof(flight.flightcode))) {
printf("Too many characters.\n");
continue;
}
int i = 0;
if (flight.flightcode[i] == 0) {
printf("Empty input.\n");
continue;
}
if (flight.flightcode[i] < 'A' || flight.flightcode[i] > 'Z') {
printf("Character %d is not upper case.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < 'A' || flight.flightcode[i] > 'Z') {
printf("Character %d is not upper case.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
if (flight.flightcode[i] < '0' || flight.flightcode[i] > '9') {
printf("Character %d is not a digit.\n", i);
continue;
}
i++;
if (flight.flightcode[i] == 0)
break;
}
}
Some remarks:
in your code you set correct_code to 1 as soon as the first character was ok. If you want to loop through the characters you must check if there is an error and exit the loop.
don't use ASCII codes when you have the specific character literals available.
I suggest that you take my solution and, as an exercise fix it to be able to work with arbitrary MAX_FLIGHTCODE_LEN, and possibly with arbitrary number of letters and numbers. Of course MAX_FLIGHTCODE_LEN shall be equal to their sum!
Drop the useless requirement for not using <ctype.h>, and use also <stdbool.h>, which makes the programmer intention clearer.

Passing a matrix using pointers in C

Compiles without warnings or errors, just the following code crashes when I try to either read from or write to the matrix constValues.
It needs to be passed to another function to be read from also; the function createOutputLine.
How can I point to the data held correctly so it can be modified and read from? Thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
void createOutputFile(FILE*, int, char**);
char createOutputLine(int, int, char*, char**);
int main(int argc, char **argv) {
int removeComments = 0;
FILE *file;
if (argc > 1 && strcmp(argv[1], "-i") == 0) {
if (argc > 2) {
if (!(file = fopen(argv[2], "r"))) {
printf("Error: file not found");
return -1;
}
}
else {
printf("Error: no file specified");
return -1;
}
}
else {
printf("Error: command requires -i");
return -2;
}
createOutputFile(file, argc, argv);
fclose(file);
}
void createOutputFile(FILE *file, int argc, char **argv) {
fseek(file, 0, SEEK_SET);
char *data = (char*)malloc(2000);
FILE *header;
char name[20];
char *token = strtok(argv[2], ".");
strcpy(name, strcat(token, ".o"));
FILE *output = fopen(name, "w");
char constNames[10][15];
char **constValues[10][10];
int constsStored = 0;
while (fgets(data, 2000, file) != NULL) {
for (int i = 0; i < strlen(data); i++) {
int c = i;
bool linePrinted = false;
if (data[i] == '#' && data[i + 1] == 'd') {
for (c = i; c <= i + 7; c++) {
data[c] = '\0';
} int ch = 0;
while (data[c] != ' ') {
constNames[constsStored][ch] = data[c];
data[c] = '\0';
ch++;
c++;
} ch = 0;
while (data[c] != '\n') {
**constValues[constsStored][ch] = data[c]; //this line crashes
data[c] = '\0';
ch++;
c++;
}
if (data[c] == '\n') data[c] = '\0';
constsStored++;
}
for (int ch = 0; ch <= constsStored; ch++) {
if (data[i] == constNames[ch][0]) {
int ch2 = i + 1;
int ch3 = 1;
bool isConst = false;
while (data[ch2] != ' ') {
if (data[ch2] == constNames[ch][ch3] && isConst == false) isConst = true;
ch2++;
ch3++;
}
if (isConst || data[i + 1] == ' ') {
char line[200];
line[200] = createOutputLine(i, ch, data, **constValues);
fprintf(output, "%c", line[200]);
linePrinted = true;
}
}
}
if (!linePrinted)
fprintf(output, "%c", data[i]);
}
}
fclose(output);
free(data);
}
char createOutputLine(int i, int constElem, char *data, char **constValues) {
int ch = i;
int ch2 = 0;
char temp[200];
while (data[ch] != '\n' && data[ch] != ' ' && data[ch] != ';') {
temp[ch2] = data[ch];
printf("%c", data[ch]);
ch++;
ch2++;
}
char line[200];
ch2 = 0;
for (ch = i; ch <= sizeof(data); ch++) {
line[ch2] = data[ch];
ch2++;
}
for (ch = 0; ch <= 10; ch++) {
line[ch2] = constValues[constElem][ch];
ch2++;
}
for (ch = 0; ch <= sizeof(temp); ch++) {
line[ch2] = temp[ch];
ch2++;
}
line[ch2 + 1] = '\n';
return line[200];
}
A pointer shall point to an object before it can be derefenced. Full stop.
char **constValues[10][10]; just declares an 2D array of pointers to pointers to characters. And as it is an automatic array (neither statically nor dynamically allocated), its pointers are just uninitialized.
When you late use **constValues[constsStored][ch] = data[c];, you try to dereference an uninitialized pointer which is explicitely Undefined Behaviour. You are lucky to get an immediate crash, because UB consequences can be apparently unrelated problems.
The normal way is to declare arrays of objects, and use the addresses of those objects for pointers.
That's not all: C arrays are not first class citizens. You cannot assign to array, nor return it from a function. So this is plain wrong:
char line[200];
line[200] = createOutputLine(i, ch, data, **constValues);
It just assigns the unique character returned by the function past the end of the array!
So is this:
char line[200];
...
return line[200];
It does not return an array (C does not allow it) but the value of the byte that happens to live past the array.
I am sorry, but there are too many errors for me to fix them is such a long program.
You may find C hard and ask for help. But build small code containing only what you want to work on. And only when those small pieces work correctly, try to assemble them in a larger program.

Replace space with newline

I'm trying to do a space with line replacer for Ti-89 calculators so that I can print lines without them being cut because of the horizontal character length. They would normally look like so
This is a pretty long test
ing for the Ti89 Calculator
and I would like them to look like so
This is a pretty long
testing for the Ti89
Calculator
I tried to do it with this code
void _print_line(char* string)
{
int k = strlen(string);
if(k > 26)
{
int n = 0;
int c = 25;
while(n == 0)
{
if(string[c] == 32)
{
n = 1;
}
else
{
c--;
}
if(c <= 0)
{
n = 2;
}
}
if(n == 1)
{
string[c] == '\n';
}
}
printf("%s\n", string);
}
But it seems to just ignore it and keep printing it like the first example.
You are not inserting the carriage return.
Replace
string[c] == '\n';
With
string[c] = '\n';
As Till said, you are not inserting the carriage return. The line
string[c] == '\n';
needs to be
string[c] = '\n';
With the difference being one "equals" sign versus two.
The reason is because "==" is a conditional operator which evaluates to either true or false, while "=" is the assignment operator which sets a value to a variable.
There needs to be processed for the entire string.
Keep a record of the current output character position to check whether or not exceed the specified width when you output the next word.
like this:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define DISP_OUT stdout
#define DISP_WIDTH 26
int disp_one(char ch){
static int pos;
fputc(ch, DISP_OUT);
if(ch == '\n')
pos = 0;
else
++pos;
if(pos == DISP_WIDTH){
fputc('\n', DISP_OUT);
pos = 0;
}
return pos;
}
typedef enum word_break {
KEEP, BREAK
} WORD_BREAK;
void disp(const char *str, WORD_BREAK word_break){
static int pos;
switch(word_break){
case BREAK:
while(*str){
pos = disp_one(*str++);
}
break;
case KEEP:
while(*str){
if(isspace((unsigned char)*str)){
pos = disp_one(*str++);
continue;
}
const char *end = str;//end : word end (find delimiter)
while(*end && !isspace((unsigned char)*end))
++end;
int len = end - str;//length of next output word
if(pos + len >= DISP_WIDTH && len < DISP_WIDTH){
pos = disp_one('\n');
}
while(str < end){
pos = disp_one(*str++);
}
}
break;
}
}
int main(void){
char *text = "This is a pretty long testing for the Ti89 Calculator";
disp(text, BREAK);
disp("\n", BREAK);
disp(text, KEEP);
return 0;
}

Better Way Without Goto?

I have a program where my code uses a goto statement, and I want to get rid of it in a nice way, but I can't seem to find a solution. If goto is the best way, then please let me know. Here is a summary of the code:
//Counts how many times every word appears in a file
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUMWORDS 1000
#define WORDLEN 50
typedef struct
{
char word[WORDLEN + 1];
int num;
} Appearance;
int main(int argc, char *argv[]) {
FILE *readfile;
Appearance *appearlist[NUMWORDS] = {NULL};
char word[WORDLEN + 1];
int i;
//Get a valid filename and open the file, store pointer into readfile
...
char c;
while (c != EOF) {
skip: //Annoying label
//Get a word from readfile, store into word
...
if (word[0] != '\0') {
for (i = 0; i < NUMWORDS && appearlist[i]; i++) {
if (strcmp(appearlist[i] -> word, word) == 0) {
appearlist[i] -> num++;
goto skip; //Annoying goto
}
}
appearlist[i] = (Appearance *) malloc(sizeof(Appearance));
appearlist[i] -> num = 1;
strcpy(appearlist[i] -> word, word);
}
}
//Display results, free memory
...
return 0;
}
The problem is, I want to skip code that is outside of the loop I want to skip from. I would like to not create another variable only designed for this. If you want the full code, click on "Show code snippet."
//Counts how many times every word appears in a file
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUMWORDS 1000
#define WORDLEN 50
#define FILENAMELEN 50
typedef struct
{
char word[WORDLEN + 1];
int num;
} Appearance;
int main(int argc, char *argv[])
{
char filename[FILENAMELEN];
FILE *readfile;
Appearance *appearlist[NUMWORDS] = {NULL};
char word[WORDLEN + 1];
size_t ln;
int i;
if (argc == 2)
strncpy(filename, argv[1], sizeof(filename));
else {
printf("Enter a filename to count appearances from, or just press enter to quit: ");
fgets(filename, FILENAMELEN, stdin);
ln = strlen(filename) - 1;
if (filename[ln] == '\n')
filename[ln] = '\0';
}
while((readfile = fopen(filename, "r")) == NULL) {
if (filename[0] == '\0')
return 0;
printf("Invalid file! Please enter another filename, or just press enter to quit: ");
fgets(filename, FILENAMELEN, stdin);
ln = strlen(filename) - 1;
if (filename[ln] == '\n') filename[ln] = '\0';
}
char c;
while (c != EOF) {
skip:
for (i = 0; (c = getc(readfile)) != EOF && (isalnum(c) || c == '\''); i++) {
if (i >= WORDLEN) {
word[i] = '\0';
printf("\nWarning: word too long (over %d characters), trimming to: %s\n", WORDLEN, word);
while ((c = getc(readfile)) != EOF && (isalnum(c) || c == '\'')) ;
} else {
word[i] = tolower(c);
}
}
word[i] = '\0';
if (word[0] != '\0') {
for (i = 0; i < NUMWORDS && appearlist[i]; i++) {
if (strcmp(appearlist[i] -> word, word) == 0) {
appearlist[i] -> num++;
goto skip;
}
}
appearlist[i] = (Appearance *) malloc(sizeof(Appearance));
appearlist[i] -> num = 1;
strcpy(appearlist[i] -> word, word);
}
}
for (i = 0; i < NUMWORDS && appearlist[i]; i++) {
printf("Word: %s, Appearances: %d\n", appearlist[i] -> word, appearlist[i] -> num);
free(appearlist[i]);
}
return 0;
}
Using goto in this case is often considered acceptable.
Alternatives would be to set a variable so that you can continue in the outer loop after breaking from the inner one, or turning the whole segment that you want to escape from into a separate function, and returning from it instead of using goto.
I'm ignoring any other issues there may be with the code that aren't relevant to the question!
Put everything beginning with the 'if' statement into a separate method (let's call it "process" and replace the goto with return. Then the while-loop becomes:
while (c != EOF) {
//Get a word from readfile, store into word
...
process(...);
}
Sometimes using goto is a hint that code should use a helper function
static bool findword(Appearance *appearlist, size_t size, const char *word) {
for (size_t i = 0; i < size && appearlist[i]; i++) {
if (strcmp(appearlist[i]->word, word) == 0) {
appearlist[i]->num++;
return true;
}
}
return false;
}
while (c != EOF) {
//Get a word from readfile, store into word
...
if (word[0] != '\0') {
if (findword(appearlist, NUMWORDS, word)) {
continue;
}
appearlist[i] = (Appearance *) malloc(sizeof(Appearance));
appearlist[i] -> num = 1;
strcpy(appearlist[i] -> word, word);
}
}

Resources