Sorry for such a mediocre question, but I ran into what seems to be a tiny problem, but simply can't get over it. For my task I have to take a line of string from a file, and put it into another file backwards, for example:
one two three
four five six
would be
three two one
six five four
My problem is, is that I'm getting
three two one
si five four
So basically the flaw is that there is a space character at the beginning of each line and the last letter of the last word is always missing. Here's my reverse function:
void reverse(char input[], int length, char output[]) {
char space = 32;
input[length - 1] = space;
int value = 0;
int i, k = 0, j;
for (i = 0; i <= length; i++) {
if (input[i] == space) {
for (j = i - 1; j >= k; j--, value++) {
output[value] = input[j];
}
if (j == -1) {
output[value] = space;
value++;
}
k = i;
}
}
char c = 0;
for (int i = 0, j = length - 1; i <= j; i++, j--) {
c = output[i];
output[i] = output[j];
output[j] = c;
}
}
What I'm doing is first reversing each word by character, and then the whole line. If someone could help me find the last bits that I've missed I would greatly appreciate it.
The flaws come from your approach:
why do you force a space at offset length - 1? If you read the line with fgets(), there is probably a newline ('\n') at the end of the line, but it might be missing at the end of the input, which would explain the x getting overwritten on the last line.
you should not modify the input buffer.
Here is a simplified version, along with a simple main function:
#include <stdio.h>
#include <string.h>
void reverse(const char *input, int length, char *output) {
int i, j, k, v;
for (i = k = v = 0;; i++) {
if (i == length || input[i] == ' ') {
for (j = i; j-- > k; v++) {
output[v] = input[j];
}
for (; i < length && input[i] == ' '; i++) {
output[v++] = ' ';
}
if (i == length) {
output[v] = '\0';
break;
}
k = i;
}
}
for (i = 0, j = length - 1; i < j; i++, j--) {
char c = output[i];
output[i] = output[j];
output[j] = c;
}
}
int main() {
char input[256];
char output[256];
while (fgets(input, sizeof input, stdin)) {
reverse(input, strcspn(input, "\n"), output);
puts(output);
}
return 0;
}
Output:
three two one
six five four
Here is a simpler reverse function that operates in one pass:
#include <string.h>
void reverse(const char *input, int length, char *output) {
int i, j, k, v;
for (i = k = 0, v = length;; i++) {
if (i == length || input[i] == ' ') {
for (j = i; j-- > k;) {
output[--v] = input[j];
for (; i < length && input[i] == ' '; i++) {
output[--v] = ' ';
}
if (v == 0) {
output[length] = '\0';
break;
}
k = i;
}
}
}
Replace input[length - 1] = space; with input[length] = space;
Related
Write a function that takes a string as a parameter and returns its words sorted in order of their length first and then in alphabetical order on line separated by '^'
here is examples of output
There will be only spaces, tabs and alphanumeric caracters in strings.
You'll have only one space between same size words and ^ otherwise.
A word is a section of string delimited by spaces/tabs or the start/end of the string. If a word has a single letter, it must be capitalized.
A letter is a character in the set [a-zA-Z]
here is my code, but it returns nothing I think issue in last function....
#include <unistd.h>
#include <stdlib.h>
int is_upper(char c)
{
return c >= 'A' && c <= 'Z';
}
int my_lower(char c)
{
if (is_upper(c))
return c + 32;
return c;
}
int my_strlen(char *s)
{
int i = 0;
for (; s[i]; i++)
;
return i;
}
int my_is(char c)
{
return c == ' ' || c == '\t';
}
char *my_strsub(char *s, int start, int end)
{
char *res = malloc(end - start);
int i = 0;
while (start < end)
res[i++] = s[start++];
res[i] = 0;
return res;
}
int cmp_alpha(char *a, char *b)
{
while (*a && *b && *a == *b)
{
a++;
b++;
}
return my_lower(*a) <= my_lower(*b);
}
int cmp_len(char *a, char *b)
{
return my_strlen(a) <= my_strlen(b);
}
void my_sort(char *arr[], int n, int(*cmp)(char*, char*))
{
char *tmp;
for (int i = 0; i < n; i++)
for (int j = 0; j < n - 1; j++)
{
if ((*cmp)(arr[j], arr[j + 1]) == 0)
{
tmp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tmp;
}
}
}
char* long(char *s)
{
int start = 0, idx = 0;
char *words[my_strlen(s) / 2 + 1];
for (int i = 0; s[i]; i++)
{
if (!my_is(s[i]) && i > 0 && my_is(s[i - 1]))
start = i;
if (my_is(s[i]) && i > 0 && !my_is(s[i - 1]))
words[idx++] = my_strsub(s, start, i);
if (!s[i + 1] && !my_is(s[i]))
words[idx++] = my_strsub(s, start, i + 1);
}
my_sort(words, idx, &cmp_alpha);
my_sort(words, idx, &cmp_len);
char* res = malloc(100);
int pushed=0;
for (int i = 0; i < idx - 1; i++)
{
res[pushed]=*words[i];
if (my_strlen(&res[pushed]) < my_strlen(&res[pushed + 1]))
{
res[pushed]=res[94];
}
else
{
res[pushed]=res[32];
}
pushed++;
}
res[pushed]='\0';
return res;
}
int main()
{
long("Never take a gamble you are not prepared to lose");
return 0;
}
Apart from the off-by-one allocation error in my_strsub, separating and sorting the words seems to work well. Only then you confuse the result character array with a character pointer array, e. g. with res[pushed]=*words[i] you write only the first character of a word to the result. The last for loop of ord_alphlong could rather be:
if (idx)
for (int i = 0; ; )
{
char *word = words[i];
int lng = my_strlen(word);
if (100 < pushed+lng+1) exit(1); // too long
for (int i = 0; i < lng; ) res[pushed++] = word[i++];
if (++i == idx) break; // last word
res[pushed++] = lng < my_strlen(words[i]) ? '^' // other size
: ' '; // same size
}
Of course in order to see the result of the function, you'd have to output it somehow.
after a long time spent trying to debug this I've come for your help.
Basically in this exercise I'm trying to read the string "31|Name1;23|Name2;15|Name3" and store it in an array of struct s_perso where the | are marking the end of an age and the beginning of a name, and where the ; are marking the beginning of a new struct.
Here's the given ft_perso.h :
#include <string.h>
#ifndef FT__PERSO__H
#define FT__PERSO__H
typedef struct s_perso
{
char *name;
float life;
int age;
char *profession;
}
t_perso;
#endif
We will only use the datas age and name from this struct s_perso.
Here's my code :
#include "ft_perso.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int numberofstructs(char *str)
{
int i;
int length;
i = 0;
length = 0;
if (str[0])
length = 0;
else
{
while (str[i])
{
if (str[i] == ';')
length += 1;
i++;
}
}
return (length);
}
int get_data_length(char *str, int i)
{
int length;
length = 0;
while (str[i] != '|' && str[i] != ';' && str[i] != '\0')
{
length++;
i++;
}
return (length);
}
char *get_data(char *str, int i)
{
int j;
char *str2;
j = 0;
str2 = (char *)malloc(sizeof(char) * get_data_length(str, i) + 1);
while (str[i] != '|' && str[i] != ';' && str[i] != '\0')
{
str2[j] = str[i];
i++;
j++;
}
str2[j] = '\0';
return (str2);
}
t_perso **ft_decrypt(char *str)
{
int i;
int j;
t_perso **textttt_perso;
i = 0;
j = 0;
textttt_perso = (t_perso **)malloc(sizeof(t_perso **));
*textttt_perso = (t_perso *)malloc(sizeof(t_perso *) * numberofstructs(str));
while (j <= strlen(str) && str[j])
{
if (str[j] == ';')
{
i++;
j++;
}
textttt_perso[i]->age = atoi(get_data(str, j));
j = j + get_data_length(str, j) + 1;
textttt_perso[i]->name = get_data(str, j);
j = j + get_data_length(str, j);
}
textttt_perso[i+1] = 0;
return (textttt_perso);
}
int main(void)
{
int i;
t_perso **tab;
i = 0;
char str[29] = "31|Name1;23|Name2;15|Name3";
tab = ft_decrypt(str);
while(i <= numberofstructs(str))
{
printf("age = %d\n", tab[i]->age);
printf("age = %s\n", tab[i]->.name);
i++;
}
}
From my debugging, I get the segfault error on the second call (when i = 1 and we are working on the substring 23) instruction of t_perso **ft_decrypt(char *str) :
textttt_perso[i]->age = atoi(get_data(str, j));
My guess is that my allocation of memory either for the array of struct in itself or the number of arrays it can contain is wrong. I can't point my finger on the problem tho...
Thanks in advance for your help, have a nice day !
You never allocate space for an actual structure. In your example:
textttt_perso = (t_perso **)malloc(sizeof(t_perso **));
allocates space for one pointer and:
*textttt_perso = (t_perso *)malloc(sizeof(t_perso *) * numberofstructs(str));
allocates enough space for 3 pointers. At some point you need to allocate space for the actual structures.
You also have other issues. In numberofstructs() you have if(str[0]) that will cause length to always be zero. Also in numberofstructs(), you count the semi-colons. If there is data after the last sem-colon you would need to add 1 to length.
You have many other issues in this code that will show up if the data isn't perfect but here is an implementation of ft_decrypt that should work. Initial malloc should be to hold the array of pointers. Then the loop should allocate a structure for each array entry.
t_perso** ft_decrypt(char* str)
{
int i = 0;
int j = 0;
t_perso** textttt_perso;
textttt_perso = malloc(sizeof(*textttt_perso) * numberofstructs(str));
while (j <= strlen(str) && str[j])
{
if (str[j] == ';')
{
i++;
j++;
}
textttt_perso[i] = malloc(sizeof(*textttt_perso[i]));
textttt_perso[i]->age = atoi(get_data(str, j));
j = j + get_data_length(str, j) + 1;
textttt_perso[i]->name = get_data(str, j);
j = j + get_data_length(str, j);
}
return (textttt_perso);
}
I have following function in c code
void analyze_text(char text[]) {
...
for (int i = 0; i < text_length || text[i] != '\0'; i++) {
...
}
}
In main function i would like to pass some string to it. If i do something like this
char text[4000] = "some text here";
analyze_text(text);
this is cool and do the goal, but i would like to have some user input present and I am not sure how to get char[] out of it. I tried following 2 and none of them seemed to work:
char text[4000];
scanf("%s",text);
analyze_text(text);
OR
char text[4000];
int c;
int count=0;
c = getchar();
count = 0;
while ((count < 4000) && (c != EOF)) {
text[count] = c;
++count;
c = getchar();
}
analyze_text(text);
I know that the first one should return pointer to char array, but second one should return char array itself, or not?
Its been like 10 years since i havent been working with c/c++. Can anybody give me some hint please?
update (whole function):
void analyze_text(char text[]) {
int printable_text_length = 0;
int text_length = strlen(text);
int word_count = 0;
int sentence_count = 0;
int in_sentence = 0;
int in_word = 0;
int count[ASCII_SIZE] = { 0 };
for (int i = 0; i < text_length || text[i] != '\0'; i++) {
int c = text[i];
if (!isspace(c)) {
printable_text_length++;
}
if (isalpha(c)) {
in_word = 1;
in_sentence = 1;
count[tolower(c)]++;
}
if (text[i] == ' ' && text[i + 1] != ' ' && in_word==1) {
word_count++;
in_word = 0;
}
if (text[i] == '.' && in_sentence==1) {
sentence_count++;
in_sentence = 0;
}
}
if (in_word == 1) { word_count++; }
if (in_sentence == 1) { sentence_count++; }
char charIndexes[ASCII_SIZE];
for (int i = 97; i <= 122; i++) {
charIndexes[i] = i;
}
for (int i=97; i <= 122; i++) {
for (int j = i + 1; j <= 122; j++) {
if (count[i] > count[j]) {
int temp = count[j];
count[j] = count[i];
count[i] = temp;
int temp2 = charIndexes[j];
charIndexes[j] = charIndexes[i];
charIndexes[i] = temp2;
}
}
}
...printf...
}
The issue with
char text[4000];
scanf("%s",text);
analyze_text(text);
is that scanf identifies space-separated chunks, so you'll only read the first one.
In order to read up to a whole line from the user, try fgets:
char text[4000];
fgets(text, 4000, stdin);
analyze_text(text);
You may want to check the return value of fgets for error detection.
You can use dyanamic array of char to pass it into the function.
Here is the code
#include <stdio.h>
#include <stdlib.h>
void analyze_text(char* text) {
for (int i = 0; text[i] != '\0'; i++) {
printf("%c\n",text[i] );
}
}
int main() {
char* text = (char *)malloc(4000 * sizeof(char));
scanf("%s", text);
analyze_text(text);
return 0;
}
and here is the output with input = 'abhishek'
a
b
h
i
s
h
e
k
remember that strlen in dyanamc array will not give the length of input array.
I want to display all substrings with k letters, one per line, but avoid duplicate substrings. I managed to write to a new string all the k length words with this code:
void subSent(char str[], int k) {
int MaxLe, i, j, h, z = 0, Length, count;
char stOu[1000] = {'\0'};
Length = (int)strlen(str);
MaxLe = maxWordLength(str);
if((k >= 1) && (k <= MaxLe)) {
for(i = 0; i < Length; i++) {
if((int)str[i] == 32) {
j = i = i + 1;
} else {
j = i;
}
for(; (j < i + k) && (Length - i) >= k; j++) {
if((int)str[j] != 32) {
stOu[z] = str[j];
} else {
stOu[z] = str[j + 1];
}
z++;
}
stOu[z] = '\n';
z++;
}
}
}
But I'm struggling with the part that needs to save only one time of a word.
For example, the string HAVE A NICE DAY
and k = 1 it should print:
H
A
V
E
N
I
C
D
Y
Your subSent() routine poses a couple of challenges: first, it neither returns nor prints it's result -- you can only see it in the debugger; second it calls maxWordLength() which you didn't supply.
Although avoiding duplicates can be complicated, in the case of your algorithm, it's not hard to do. Since all your words are fixed length, we can walk the output string with the new word, k letters (plus a newline) at a time, doing strncmp(). In this case the new word is the last word added so we quit when the pointers meet.
I've reworked your code below and added a duplication elimination routine. I didn't know what maxWordLength() does so I just aliased it to strlen() to get things running:
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define maxWordLength strlen
// does the last (fixed size) word in string appear previously in string
bool isDuplicate(const char *string, const char *substring, size_t n) {
for (const char *pointer = string; pointer != substring; pointer += (n + 1)) {
if (strncmp(pointer, substring, n) == 0) {
return true;
}
}
return false;
}
void subSent(const char *string, int k, char *output) {
int z = 0;
size_t length = strlen(string);
int maxLength = maxWordLength(string);
if (k >= 1 && k <= maxLength) {
for (int i = 0; i < length - k + 1; i++) {
int start = z; // where does the newly added word begin
for (int j = i; (z - start) < k; j++) {
output[z++] = string[j];
while (string[j + 1] == ' ') {
j++; // assumes leading spaces already dealt with
}
}
output[z++] = '\n';
if (isDuplicate(output, output + start, k)) {
z -= k + 1; // last word added was a duplicate so back it out
}
while (string[i + 1] == ' ') {
i++; // assumes original string doesn't begin with a space
}
}
}
output[z] = '\0'; // properly terminate the string
}
int main() {
char result[1024];
subSent("HAVE A NICE DAY", 1, result);
printf("%s", result);
return 0;
}
I somewhat cleaned up your space avoidance logic but it can be tripped by leading spaces on the input string.
OUTPUT
subSent("HAVE A NICE DAY", 1, result);
H
A
V
E
N
I
C
D
Y
subSent("HAVE A NICE DAY", 2, result);
HA
AV
VE
EA
AN
NI
IC
CE
ED
DA
AY
subSent("HAVE A NICE DAY", 3, result);
HAV
AVE
VEA
EAN
ANI
NIC
ICE
CED
EDA
DAY
I am trying to read the input to my program (a string of chars) and invert the order of the words that are in it.
For example, if I were to pass my program ABC DEF GHI JKL it would output JKL GHI DEF ABC. I am using the whitespace as separators.
My code:
char toReverse[1000];
char outputArray[1000];
int charCount = //Size of the toReverse array. Varies on the input
//It is the total number of chars stored in the array
...
int i;
int tempCharCount = charCount;
int wordSize = 0;
int outputIndex = 0;
int sentenceIndex = 0;
int charStep = 0;
for(i = charCount-1; i>=0; i--){
if(toReverse[i] == ' '){
int j;
sentenceIndex = tempCharCount - wordSize;
for(j = 0; j<charStep; j++){
outputArray[outputIndex++] = toReverse[sentenceIndex++];
}
outputArray[outputIndex] = ' ';
outputIndex++;
charStep = 0;
}
wordSize++;
charStep++;
}
There is a flaw in my code. I do know why this happens though. For example, if I were to pass as input ABC DEF GHI, it will only output GHI DEF. This is because whenever the outer for loop reaches index 0 of my toReverse array, since it is not a space ' ', it does not do the if(toReverse[i]) inner for(j = 0; j<charStep; j++) since the condition is not met.
Do you have any advice regarding to the logic? I have tried reversing my logic, such as if(toReverse[i] != ' ') but it brings more problems than it solves.
Thanks for your advice and comments!
Cheers
Edit 1
I am reading my input from a file By the way!
Update 1
Here I am basically trying to open various files and read chars from them
int main(int argc, char * argv[])
{
int i = 1;
FILE * fp = NULL;
if(1==argc){
do_read(stdin);
}else{
// cycle through all files in command line arguments and read them
for (i=1; i < argc; i++) {
if ((fp = fopen(argv[i], "r")) == NULL) {
printf("Failed to open file.\n");
}
else {
do_read(fp);
fclose(fp);
}
}
}
//printf("\n");
//printf("\n");
printf("%i",charCount);
return 0;
}
sample
#include <stdio.h>
#include <string.h>
void proc_rev(char toReverse[], char outputArray[]){
int charCount = strlen(toReverse);
int i;
int tempCharCount = charCount;
int wordSize = 0;
int outputIndex = 0;
int sentenceIndex = 0;
int charStep = 0;
for(i = charCount-1; i>=0; i--){
if(toReverse[i] == ' '){
int j;
sentenceIndex = tempCharCount - wordSize;
for(j = 0; j<charStep; j++){
outputArray[outputIndex++] = toReverse[sentenceIndex++];
}
outputArray[outputIndex] = ' ';
outputIndex++;
charStep = 0;
}
wordSize++;
charStep++;
}
outputArray[outputIndex] = '\0';
}
int main(void){
FILE *fp = stdin;
char toReverse[1000] = " ";
char outputArray[1000];
while(1 == fscanf(fp, "%998[^\n]%*c", &toReverse[1])){
proc_rev(toReverse, outputArray);
puts(outputArray);
}
return 0;
}
void do_read(FILE *fp){
char toReverse[1000] = " ";
char outputArray[1000];
while(1 == fscanf(fp, "%998[^\n]%*c", &toReverse[1])){
proc_rev(toReverse, outputArray);
puts(outputArray);
}
}
This code is not tested but basic idea is reversing the whole string once and then reverse it word by word. idea is correct, implementation may have bugs
void swap(char* s, int i, int j) {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
void rev(char* stirng, int start, int len) {
for (int i=0; i<len/2; ++i) {
swap(string, i, len-i-1);
}
}
int main() {
char* string = read from file
int len = strlen(string);
rev(string, 0, len);
for (int i=0; i<len;) {
int l = 0;
int j=i;
while (j<len && string[j]!=' ') ++j;
rev(string, i, j-i);
i=j+1;
}
}
The current logic reverses individual words from last to second word. However to reverse the first word you will have to add a check apart from
if(toReverse[i] == ' ')
as the first character need not be a space.
A separate check can be used when counter 'i' reaches zero, i.e. first character