#c #newline #command-line-arguments #system-calls #file-handling
#c #новая строка #аргументы командной строки #системные вызовы #обработка файлов
Вопрос:
Я хочу создать текстовый файл с несколькими строками, используя системные вызовы на C, и заполнить его текстом, предоставленным в качестве аргументов командной строки.
Это то, что я написал:
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_SZ 1024
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid Number of argumentsn");
printf("USAGE: ./a.out file_name "msg"n");
} else {
int fd_creat, fd_open, fd_write;
char file_name[MAX_SZ];
char *msg = (char *)malloc(strlen(argv[2]) * sizeof(char));
strcpy(file_name, argv[1]);
fd_creat = creat(file_name, 0777);
if (fd_creat < 2) {
printf("ERROR: File could not be createdn");
} else {
fd_open = open(file_name, O_WRONLY);
strcpy(msg, argv[2]);
fd_write = write(fd_open, msg, strlen(msg));
close(fd_open);
}
}
return 0;
}
Если я выполню эту программу как:
./a.out test.txt "FoonBar"
Он записывает все это в test.txt как оно есть. В принципе, я хочу, чтобы «Foo» и «Bar» были в отдельных строках.
Комментарии:
1. Возможно, вы захотите использовать
PATH_MAX
для путей. При этом, почему вы копируете аргумент в буфер, но не меняете его? Чем это лучше, чем простоargv[1]
прямое использование?2. Вы делаете это снова с
argv[2]
помощью . Возможно, это результат ошибочного предположения, что вам нужно скопироватьargv
значения перед их использованием, но это не так.3. Также нет необходимости вызывать
creat
. Просто откройте файл в режиме записи и начните запись. В C вы, как правило, захотите использовать функции наFILE*
основе, напримерfopen
, вместо низкоуровневойopen
функции.4. Вам нужен еще один элемент для обозначения нулевого символа
msg
.5. Чтобы сделать код читаемым без ненужного выделения и копирования памяти, просто выполните
char *file_name = argv[1];
иchar *msg = argv[2];
Ответ №1:
Здесь есть две проблемы:
- То, как вы обрабатываете аргументы и не выделяете достаточно памяти для задействованных данных,
- Интерпретация escape-последовательностей, подобных
n
правильной, поскольку оболочка предоставит их вам как есть, в необработанном виде.
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
// This moves overlapping strings from src -> dest but only
// if dest is before src
void cc_str_drag(char* dest, char* src) {
while (*dest) {
*dest = *src;
dest;
src;
}
}
// This interprets the n sequence and can be extended to handle others, like
// t, \, or even g.
void cc_interpret(char* str) {
for (;*str; str) {
// If this is a sequence start...
if (*str == '\') {
// ...find out which one...
switch (str[1]) {
case 'n':
// Shift back...
cc_str_drag(str, amp;str[1]);
// ...and replace it.
*str = 'n';
break;
}
}
}
}
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid Number of argumentsn");
// Remember argv[0] is the name of the program
printf("USAGE: %s file_name "msg"n", argv[0]);
return -1;
}
// Since it's not the 1970s, use fopen() and FILE*
FILE* output = fopen(argv[1], "w");
if (!output) {
printf("ERROR: File could not be createdn");
return -2;
}
// Copying here to avoid tampering with argv
char* str = strdup(argv[2]);
// Replace any escape sequences
cc_interpret(str);
// Then just dump it directly into the file
fwrite(str, 1, strlen(str), output);
fclose(output);
return 0;
}
Обратите внимание на используемые здесь инструменты:
strdup
это более быстрый способ копирования строки C, чемmalloc(strlen(s))
ее последующее копирование. Это вызывает страшные единичные ошибки.FILE*
работает намного лучше, потому что он буферизован.open()
используется для низкоуровневых операций, которые нельзя буферизировать. Знайте, когда использовать какой инструмент.- Не бойтесь писать функции, которые управляют строковым содержимым. Строки C действительно важно понимать, а не бояться.
Комментарии:
1. В вашем коде была одна небольшая ошибка, хотя для ввода: «Foo nBar», ваш код выдавал «Foo» и «arr» в отдельных строках, я изменил «strcpy (str, amp;str[1]);» с «strcpy (amp;str[1], amp;str [2]);» чтобы получить желаемый результат («Foo» и «Bar» в отдельных строках). Спасибо за вашу помощь!
2. Я протестировал это как есть, и это сработало для меня, создав правильный вывод и для этого, так что, возможно, что-то немного отличается в вашей версии. Рад, что это сработало! Возможно, что использование
strcpy()
для перемещения объектов в такой строке недопустимо, я написал свой собственный транспозиционер строк, но удалил его в последнюю секунду, подумав, что этоstrcpy
может сработать.3. В документации указано: «Поведение не определено, если строки перекрываются». это то, о чем я беспокоился.
4. Я вернул туда функцию перемещения строк.