user-0 提问: ls命令输出格式算法

回复:
user: user-1
content: 理解int idx = filesno / ((files_index + i) / (i + 1));这类公式/算法的方法之一： 写一小段能独立运行的代码，里面调用该公式（必要时将该公式拆为若干小步，并将每步的中间结果输出），让该公式的各个自变量在一定范围内循环加一。并将输出结果重定向到一个文本文件。

user: user-2
content: 如果想简单实现，思路并不复杂。终端的长宽的字节数可以用函数ioctl获得，或者从shell的$LINES, $COLOWNS获得。然后把文件名按字母表排序，然后再根据文件名的长度决定每行显示几个文件，显示多少行。最后一步比较费劲。

user: user-1
content: 引用 4 楼 u012786370 的回复:[quote=引用 3 楼 zhao4zhong1 的回复:] 为什么不直接去看Linux源代码中的ls命令怎么实现的呢？ 那又为什么不自己先想一想呢？其实我正在看，告诉你吧，那个ls源码4000多行，还不算外部包含的[/quote] 你凭一己之力又怎么可能敌得过ls源代码自诞生以来到今天所经历的时间和一干作者和补丁者的智商呢？恐怕你学习都不一定学的过来。

user: user-0
content: 引用 10 楼 zhao4zhong1 的回复:我猜ls源代码中的实现绝对比9楼的思路复杂得多，且还能处理很多事先想象不到的各种意外情况（比如文件名中包含Tab字符、汉字、图形符号、……）。 引用 9 楼 ForestDB 的回复:首先得到当前目录下所有文件名，不算太难吧 然后排序，会吧？ 排序同时可以得到最长的文件名的长度，比如39好了 那么一行（按80列算，这个80也是能动态取到的），可以放两个文件名（39加一个空格） 然后对于排好序的文件名列表，依次输出 %-40s %-40s \n 结束  可以写出来了么？ 这个是我从ls源码中摘出来的大概的一个算法，谁能给我解释一下这条语句为什么这样写  int idx = filesno / ((files_index + i) / (i + 1));  下面是全部代码，因为符合的是POSIX标准，所以在windows下不能编译，可以在linux下编译  #include <stdio.h> #include <sys/types.h> #include <sys/ioctl.h> #include <unistd.h> #include <termios.h> #include <stdlib.h> #include <string.h>  /* Information about filling a column.  */ struct column_info {     int valid_len;     int line_len;     int *col_arr; }; struct column_info *column_info;  static int line_length;  /* Maximum number of columns ever possible for this display.  */ static int max_idx;  /* The minimum width of a colum is 3: 1 character for the name and 2 for the separating white space.  */ #define MIN_COLUMN_WIDTH	3  static inline int cmp_str(char const *lhs, char const *rhs, int(*cmp)(char const *, char const *)) {     return cmp(lhs, rhs); }  static int compare_str(const void *lhs, const void *rhs) {     return cmp_str(*(char **)lhs, *(char **)rhs, strcoll); }  unsigned short int get_ws_col() {     struct winsize size;     ioctl(STDIN_FILENO, TIOCGWINSZ, &size);     return size.ws_col; }  static void init_column_info(void) {     int i;     int allocate = 0;      line_length = get_ws_col();     max_idx = line_length / MIN_COLUMN_WIDTH;     if (max_idx == 0)         max_idx = 1;      if (column_info == NULL)     {         column_info = malloc(sizeof(struct column_info) * max_idx);         allocate = 1;     }      for (i = 0; i < max_idx; ++i)     {         int j;          column_info[i].valid_len = 1;         column_info[i].line_len = (i + 1) * MIN_COLUMN_WIDTH;          if (allocate)             column_info[i].col_arr = (int *)malloc(sizeof(int) * (i + 1));          for (j = 0; j <= i; ++j)             column_info[i].col_arr[j] = MIN_COLUMN_WIDTH;     } }  static void indent(int from, int to) {     while (from < to)     {         putchar(' ');         from++;     } }  static void print_many_per_line(char *arr[], size_t files_index) {     struct column_info *line_fmt;     int filesno;			/* Index into files. */     int row;				/* Current row. */     int max_name_length;	/* Length of longest file name + frills. */     int name_length;		/* Length of each file name + frills. */     int pos;			/* Current character column. */     int cols;			/* Number of files across. */     int rows;			/* Maximum number of files down. */      int max_cols;      init_column_info();     /* Normally the maximum number of columns is determined by the     screen width.  But if few files are available this might limit it     as well.  */     max_cols = max_idx > files_index ? files_index : max_idx;      /* Compute the maximum number of possible columns.  */     for (filesno = 0; filesno < files_index; ++filesno)     {         int i;          name_length = strlen(arr[filesno]);          for (i = 0; i < max_cols; ++i)         {             if (column_info[i].valid_len)             {                 int idx = filesno / ((files_index + i) / (i + 1));                 int real_length = name_length + (idx == i ? 0 : 2);                 if (real_length > column_info[i].col_arr[idx])                 {                     column_info[i].line_len += (real_length                         - column_info[i].col_arr[idx]);                     column_info[i].col_arr[idx] = real_length;                     column_info[i].valid_len = column_info[i].line_len < line_length;                 }             }         }      }      /* Find maximum allowed columns.  */     for (cols = max_cols; cols > 1; --cols)     {         if (column_info[cols - 1].valid_len)             break;     }      line_fmt = &column_info[cols - 1];      /* Calculate the number of rows that will be in each column except possibly     for a short column on the right. */     rows = files_index / cols + (files_index % cols != 0);      for (row = 0; row < rows; row++)     {         int col = 0;         filesno = row;         pos = 0;         /* Print the next row.  */         while (1)         {             printf("%s", arr[filesno]);             name_length = strlen(arr[filesno]);             max_name_length = line_fmt->col_arr[col++];              filesno += rows;             if (filesno >= files_index)                 break;              indent(pos + name_length, pos + max_name_length);             pos += max_name_length;         }         putchar('\n');     } }  void main() {      char *arr[] =     {         "asdfasdf",         "adfasdf",         "sdfdaasd",         "sdfasdfasdg",         "ghjsdfa",         "asdfadsf",         "dgfsdg",         "文件",         "gsdgsrg",         "sdfgs",         "dfgsdhsdfgj",         "sdfg",         "qwersdfgsdfhs",         "sdfhfhdfgh",         "sdfg",         "dfgsdfg",         "sdfg",         "dsfgsdfg",         "电影",         "gsdfgsdfg",         "sdfgsdf",         "游戏",         "sdfgsdfg",         "dfghdfgjd",         "dfgh",         "sdfg",         "rsdfhfdgh",         "sdfhdf",         "dfgh",         "syutdfhsgd",         "ksdfgdsf",         "fgsdh",         "sdfh",         "yuosdfhsf",         "vbdfh",         "sdjklfhs",         "erfhsdfh",         "sdfh",         "sdfhsdfh",         "sdfh",         "wery",         "weywery",         "dfgh",         "sdfg",         "sdfhfdgh",         "sdfhdf",         "dfgh",         "sdfhsgd",         "sdfgdsf",         "sdfgsdh",         "sdfh",         "sdfhsf",         "sdfh",         "retfhs",         "fhsdfh",         "yudfh",         "sdfhsdfh"     };      qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), (__compar_fn_t)compare_str);     print_many_per_line(arr, sizeof(arr) / sizeof(arr[0]));      return; }

user: user-1
content: 我猜ls源代码中的实现绝对比9楼的思路复杂得多，且还能处理很多事先想象不到的各种意外情况（比如文件名中包含Tab字符、汉字、图形符号、……）。

user: user-3
content: 首先得到当前目录下所有文件名，不算太难吧 然后排序，会吧？ 排序同时可以得到最长的文件名的长度，比如39好了 那么一行（按80列算，这个80也是能动态取到的），可以放两个文件名（39加一个空格） 然后对于排好序的文件名列表，依次输出 %-40s %-40s \n 结束  可以写出来了么？

user: user-4
content: 引用 4 楼 u012786370 的回复:[quote=引用 3 楼 zhao4zhong1 的回复:] 为什么不直接去看Linux源代码中的ls命令怎么实现的呢？ 那又为什么不自己先想一想呢？其实我正在看，告诉你吧，那个ls源码4000多行，还不算外部包含的[/quote]4000行，还好吧

user: user-1
content: 理解和讨论之前请先学会如何观察！ 想知道windows下dir命令的源码多少行吗？

user: user-0
content: 引用 3 楼 zhao4zhong1 的回复:为什么不直接去看Linux源代码中的ls命令怎么实现的呢？ 那又为什么不自己先想一想呢？其实我正在看，告诉你吧，那个ls源码4000多行，还不算外部包含的

user: user-1
content: 为什么不直接去看Linux源代码中的ls命令怎么实现的呢？

user: user-0
content: 简化一下，就是如何将一个字符串数组按如上方式输出？

user: user-0
content: 难道没人会吗？