C# 如何按文件夹对文件(字符串)列表进行分组,并对组执行select方法
我正在为我们项目中的图像创建一个常量cs文件 所以我有一个这样的文件列表:C# 如何按文件夹对文件(字符串)列表进行分组,并对组执行select方法,c#,linq,lambda,C#,Linq,Lambda,我正在为我们项目中的图像创建一个常量cs文件 所以我有一个这样的文件列表: var SourceFiles = new List<String>() { "Images/BankLogos/ic_card_amex.svg", "Images/BankLogos/ic_nocards.svg", "Images/Cars/Toyota_Auris_TS_Estate.png", "
var SourceFiles = new List<String>() {
"Images/BankLogos/ic_card_amex.svg",
"Images/BankLogos/ic_nocards.svg",
"Images/Cars/Toyota_Auris_TS_Estate.png",
"Images/Icons/ic_current_location_circle.svg",
"Images/Icons/ic_abn_partially_available_circle.svg",
"Images/ic_menu.svg",
};
我有一些代码来写外部部分
private string GenerateCode(IEnumerable<string> files)
{
var content = string.Join(
$"{Environment.NewLine}\t",
files.Select(GenerateProperty));
var code = $@"
// Generated code, do not edit.
namespace Common
{{
public static class Constants
{{
{content}
}}
}}";
return code;
}
private static string GenerateProperty(string file)
{
var ext = GetExt(file);
var withoutExt = RemoveExt(ext, file);
var name = LetterOrDigit(withoutExt);
var v = file.Replace("\\", "\\\\");
return string.Format(
"public static readonly string {0} = \"{1}\";",
name,
v
);
}
private string GenerateCode(IEnumerable文件)
{
var content=string.Join(
$“{Environment.NewLine}\t”,
选择(GenerateProperty));
变量代码=$@”
//生成的代码,不要编辑。
名称空间公用
{{
公共静态类常量
{{
{content}
}}
}}";
返回码;
}
私有静态字符串生成器属性(字符串文件)
{
var ext=GetExt(文件);
var withoutExt=RemoveExt(ext,file);
变量名称=LetterOrdGit(无文本);
var v=file.Replace(“\\”,“\\\”);
返回字符串格式(
“公共静态只读字符串{0}=\”{1}\”;“,
名称
v
);
}
因此,我想如果我可以将列表分组,然后在GenerateProperty
中选择并传递列表,我可以像GenerateCode
那样写出每个部分
因此,我认为我可以管理大部分内容,但我不确定谁应该按文件夹对文件/字符串进行分组,然后在该列表上执行
选择。我使用了很多扩展方法,因此我使用现有的扩展库构建了我的答案
首先,扩展:
public static class StringExt {
// return new string consisting only of letter or digit characters from s
public static string LetterOrDigit(this string s) => s.Where(c => Char.IsLetterOrDigit(c)).Join();
// build new string from n copies of s
public static string Repeat(this string s, int n) => new StringBuilder(s.Length * n).Insert(0, s, n).ToString();
// return s upto (stopping before) the first occurrence of stopRE
// or all of s if stopRE is not present
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
return m.Success ? s.Substring(0, m.Index) : s;
}
// return s past the first occurrence of stopRE
// or all of s if stopRE is not present
public static string Past(this string s, Regex startRE) {
var m = startRE.Match(s);
return m.Success ? s.Substring(m.Index + m.Length) : s;
}
// return true if re is present in (matches) s
public static bool IsMatch(this string s, Regex re) => re.IsMatch(s);
}
public static class IEnumerableExt {
// return a new string by joining the chars together
public static string Join(this IEnumerable<char> chars) => String.Concat(chars); // faster >= .Net Core 2.1
// return a new string by combining the elements of s, separated by sep
public static string Join(this IEnumerable<string> s, string sep) => String.Join(sep, s);
}
接下来是一个新的helper方法,它递归地从路径生成类。代码有点复杂,因为我使用了LINQ,所以我可以使用Join
方法在节之间插入换行符,这样就不会有额外的空行。通常,您可以读取enumerable。选择(v=>
作为类似于foreach(enumerable中的var v)
那么,你的问题是什么?@PeterCsala我已经编辑了这个问题…谢谢..你的Cars
成员似乎有错误的名称。请尝试关键字T4
,它可以在你保存问题时自动执行file@MichaelMao我正在使用VS for mac和EnvDTE ism,但还没有完全可用。哇,谢谢,这非常有效,我会接受的……不过,我不懂GenerateProperties
,我以为我懂Linq,但它比我习惯的要复杂得多。请你能在大多数行上发表一些评论吗?正如我所说的,不管怎样,我都乐意接受。此外,我还将选项卡更改为\t,这会把事情搞砸…:(@Jules I将添加一些注释。您应该能够用“\t”
替换四个空格,它应该可以很好地工作-我一开始就这样做了。@Jules I添加了注释(可能有帮助)并使用了“\t”
,并将开始的深度修改为1
,以便更好地处理选项卡。谢谢
public static class StringExt {
// return new string consisting only of letter or digit characters from s
public static string LetterOrDigit(this string s) => s.Where(c => Char.IsLetterOrDigit(c)).Join();
// build new string from n copies of s
public static string Repeat(this string s, int n) => new StringBuilder(s.Length * n).Insert(0, s, n).ToString();
// return s upto (stopping before) the first occurrence of stopRE
// or all of s if stopRE is not present
public static string UpTo(this string s, Regex stopRE) {
var m = stopRE.Match(s);
return m.Success ? s.Substring(0, m.Index) : s;
}
// return s past the first occurrence of stopRE
// or all of s if stopRE is not present
public static string Past(this string s, Regex startRE) {
var m = startRE.Match(s);
return m.Success ? s.Substring(m.Index + m.Length) : s;
}
// return true if re is present in (matches) s
public static bool IsMatch(this string s, Regex re) => re.IsMatch(s);
}
public static class IEnumerableExt {
// return a new string by joining the chars together
public static string Join(this IEnumerable<char> chars) => String.Concat(chars); // faster >= .Net Core 2.1
// return a new string by combining the elements of s, separated by sep
public static string Join(this IEnumerable<string> s, string sep) => String.Join(sep, s);
}
private static string GenerateProperty(string file) {
var ext = Path.GetExtension(file);
var withoutExt = Path.GetFileNameWithoutExtension(file);
var name = withoutExt.LetterOrDigit();
var v = file.Replace("\\", "\\\\");
return $"public static readonly string {name} = \"{v}\";";
}
static Regex sepRE = new Regex(@"[/\\]", RegexOptions.Compiled);
private static string GenerateProperties(IEnumerable<string> files, int nestDepth = 1) {
return Generator(files.Select(f => (full: f, left: f)), nestDepth);
// internal nested method to convert list of paths to class declarations and member property declarations
// calls self recursively to handle sub-folders in paths
string Generator(IEnumerable<(string full, string left)> files, int nestDepth) {
string tabs = "\t".Repeat(nestDepth + 2);
// group tuples of (full path, rest of path after first folder) by first path folder
var ans = files.GroupBy(n => n.left.UpTo(sepRE), n => (n.full, left: n.left.Past(sepRE)))
// foreach (var fg in files.GroupBy...) -- fg is group of paths in a folder
// build class declaration from folder
.Select(fg => $"{tabs}public static class {fg.Key.LetterOrDigit()}\n{tabs}{{\n" +
// group (separate) sub-paths into paths in further folders and paths in root of this folder/class
fg.GroupBy(n => n.left.IsMatch(sepRE))
// foreach (var sfg in GroupBy...) -- sfg is group of subfolder paths or root paths
.Select(sfg =>
sfg.Key
// if in subfolders, call recursively to create classes for subfolders
? Generator(sfg, nestDepth + 1) // more nested classes
// if in root, just create properties for each root member path
: sfg.Select(n => tabs+" "+GenerateProperty(n.full)).Join("\n")
)
// join the sub-folder declarations and root property declarations
// separated by newlines
.Join("\n") +
// end the class declaration
$"\n{tabs}}}\n")
// join the class declarations at a level with newlines
// (creating a blank line between each class declaration)
.Join("\n");
return ans;
}
}
private string GenerateCode(IEnumerable<string> files) {
var content = string.Join(
$"{Environment.NewLine}\t",
GenerateProperties(files));
var code = $@"
// Generated code, do not edit.
namespace Common
{{
public static class Constants
{{
{content} }}
}}";
return code;
}