Pertanyaan Menentukan Panjang Char String di C - jika pengguna memasukkan isi string


Saya tahu di C Anda dapat mendeklarasikan string dan jumlah karakter seperti di bawah ini,

char mystring[50];

dengan '50' adalah jumlah karakter.

Namun, apa prosedur yang benar jika pengguna akan memasukkan isi string (melalui scanf ("% s", mystring);)? Apakah saya membiarkannya sebagai,

char mystring[0];

meninggalkannya sebagai '0' karena saya tidak tahu berapa banyak karakter yang akan dimasukkan pengguna?

Atau apakah saya lakukan,

char mystring[400];

memberikan hingga 400 karakter bagi pengguna untuk memasukkan?


5
2017-11-29 03:35


asal


Jawaban:


Anda telah menemukan masalah yang tepat dengan scanf () dan% s - apa yang terjadi ketika Anda tidak tahu berapa banyak masukan yang ada?

Jika Anda mencoba berlari char mystring[0];, program Anda akan dikompilasi dengan baik. Tapi Anda akan selalu segfault. Anda membuat array dengan ukuran 0, jadi ketika Anda mencoba untuk menempatkan sesuatu ke array itu, Anda akan segera keluar dari batas untuk string Anda (karena tidak ada memori yang akan dialokasikan) - yang merupakan segfault.

Jadi, poin 1: Anda harus selalu mengalokasikan ukuran untuk string Anda. Saya bisa memikirkan sangat sedikit keadaan (oke, tidak ada) di mana Anda ingin mengatakannya char mystring[0] daripada char *mystring.

Selanjutnya, ketika Anda menggunakan scanf, Anda tidak pernah ingin menggunakan "% s" specifier - karena ini tidak akan melakukan pemeriksaan batas pada ukuran string. jadi bahkan jika Anda memiliki:

char mystring[512];
scanf("%s", mystring);

jika pengguna memasukkan lebih dari 511 karakter (sejak 512 adalah \ 0), Anda akan keluar dari batas-batas array Anda. Cara untuk memperbaiki ini adalah:

scanf("%511s", mystring);

Ini semua untuk mengatakan bahwa C tidak memiliki fasilitas untuk secara otomatis mengubah ukuran string jika ada masukan lebih dari yang Anda harapkan. Ini adalah hal yang harus Anda lakukan secara manual.

Salah satu cara untuk mengatasi ini adalah dengan menggunakan fgets ().

Anda bisa mengatakan:

while (fgets(mystring, 512, stdin))
{
   /* process input */
}

Anda kemudian dapat menggunakan sscanf () untuk mengurai mystring

Coba kode di atas, dengan string panjang 5. Setelah 4 karakter telah dibaca, kode itu berputar lagi untuk mengambil sisa input. "Pemrosesan" dapat menyertakan kode untuk mengalokasikan kembali string menjadi ukuran yang lebih besar dan kemudian menambahkan masukan terbaru dari fgets ().

Kode di atas tidak sempurna - itu akan membuat lingkaran program Anda dan memproses setiap panjang string yang tak terbatas, sehingga Anda mungkin ingin memiliki batas internal keras pada itu (misalnya, loop maksimum 10 kali).


6
2017-11-29 04:07



Pengguna akan selalu dapat memasukkan lebih banyak karakter, sehingga meluap buffer Anda (sumber umum kerentanan keamanan). Anda bisa, bagaimanapun, menentukan "lebar lapangan" untuk scanf, seperti:

scanf("%50s", mystring);

Dalam hal ini buffer Anda harus 51 karakter, untuk memperhitungkan bidang 50 karakter ditambah terminator null. Atau buat buffer Anda 50 karakter dan beri tahu scanf 49 adalah lebarnya.


2
2017-11-29 03:48



Ada fungsi yang disebut ggets () yang bukan bagian dari pustaka C standar. Ini fungsi yang cukup sederhana. Ini menginisialisasi array char menggunakan malloc (). Kemudian membaca karakter dari stdin satu arang pada suatu waktu. Itu melacak berapa banyak karakter yang dibaca dan memperluas array char dengan realloc () ketika kehabisan ruang.

Ini tersedia di sini: http://cbfalconer.home.att.net/download/index.htm

Saya sarankan Anda membaca kode dan implementasikan ulang sendiri.


2
2017-11-29 03:48



Ini adalah kode cbfckerer (http://cbfalconer.home.att.net/download/index.htm) dengan beberapa modifikasi kecil dan dikompilasi menjadi satu file:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ggets.h"

#define INITSIZE   112  /* power of 2 minus 16, helps malloc */
#define DELTASIZE (INITSIZE + 16)

enum {OK = 0, NOMEM};

int fggets(char* *ln, FILE *f)
{
   int     cursize, ch, ix;
   char   *buffer, *temp;

   *ln = NULL; /* default */
   if (NULL == (buffer = malloc(INITSIZE))) return NOMEM;
   cursize = INITSIZE;

   ix = 0;
   while ((EOF != (ch = getc(f))) && ('\n' != ch)) {
      if (ix >= (cursize - 1)) { /* extend buffer */
         cursize += DELTASIZE;
         if (NULL == (temp = realloc(buffer, (size_t)cursize))) {
            /* ran out of memory, return partial line */
            buffer[ix] = '\0';
            *ln = buffer;
            return NOMEM;
         }
         buffer = temp;
      }
      buffer[ix++] = ch;
   }
   if ((EOF == ch) && (0 == ix)) {
      free(buffer);
      return EOF;
   }

   buffer[ix] = '\0';
   if (NULL == (temp = realloc(buffer, (size_t)ix + 1))) {
      *ln = buffer;  /* without reducing it */
   }
   else *ln = temp;
   return OK;
} /* fggets */
/* End of ggets.c */

int main(int argc, char **argv)
{
   FILE *infile;
   char *line;
   int   cnt;

   //if (argc == 2)
      //if ((infile = fopen(argv[1], "r"))) {
         cnt = 0;
         while (0 == fggets(&line, stdin)) {
            fprintf(stderr, "%4d %4d\n", ++cnt, (int)strlen(line));
            (void)puts(line);
            free(line);
         }
         return 0;
      //}
   //(void)puts("Usage: tggets filetodisplay");
   //return EXIT_FAILURE;
} /* main */
/* END file tggets.c */

Saya mengujinya dan itu akan selalu memberi Anda apa yang Anda inginkan.


1
2017-11-29 05:14



Praktek yang biasa di C adalah menggunakan sesuatu seperti GNU readline atau mungkin NetBSD editline, alias libedit. (API yang sama, implementasi dan lisensi perangkat lunak yang berbeda.)

Untuk program yang lebih sederhana atau pekerjaan rumah, Anda bisa secara teori memberikan lebar lapangan untuk scanf, tetapi praktik yang lebih normal adalah fgets() ke larik dengan lebar tetap dan kemudian jalankan sscanf() tentang itu. Dengan cara ini Anda mengendalikan jumlah baris yang dibaca.


0
2017-11-29 04:08



Sebagai contoh, jika pengguna memasukkan nama depan mereka maka Anda tidak selalu aman memaksimalkan ukuran 'mistik' sebanyak 35 karakter karena beberapa orang memiliki nama yang sangat panjang. Anda tidak ingin menjangkau kasus di mana pengguna tidak dapat memasukkan informasi yang Anda minta, secara penuh. Cara yang benar untuk melakukannya adalah dengan memiliki buffer sementara dengan ukuran yang sangat besar yang akan mencakup semua input yang mungkin oleh pengguna. Setelah pengguna memasukkan informasi dan disimpan ke dalam buffer, Anda kemudian mentransfer karakter dari buffer ke mystring sambil memotong semua ruang ekstra di ujung buffer. Anda akan dapat mengetahui ukuran yang Anda butuhkan untuk 'mystring' secara tepat dan Anda dapat membuat malloc hanya sejumlah ruang untuk itu dan membuang buffer. Dengan cara ini Anda tidak akan menggunakan string menggunakan lebih banyak memori untuk sisa program ... Anda hanya akan menggunakan string dengan jumlah memori yang Anda butuhkan.


0
2017-11-29 04:11