我使用下面的代码来分割字符串,但这需要花费很多时间。
using (StreamReader srSegmentData = new StreamReader(fileNamePath))
{
string strSegmentData = "";
string line = srSegmentData.ReadToEnd();
int startPos = 0;
ArrayList alSegments = new ArrayList();
while (startPos < line.Length && (line.Length - startPos) >= segmentSize)
{
strSegmentData = strSegmentData + line.Substring(startPos, segmentSize) + Environment.NewLine;
alSegments.Add(line.Substring(startPos, segmentSize) + Environment.NewLine);
startPos = startPos + segmentSize;
}
}
请建议我另一种方法将字符串拆分为固定大小的小块
答案 0 :(得分:12)
首先,您应该定义块大小的含义。如果您指的是具有固定数量的代码单元 的块,那么您的实际算法可能会很慢,但它可以正常工作。如果它不是您想要的,而您实际上是指具有固定数量的字符 的块,那么它就会被破坏。我在此代码审核帖子中讨论了类似的问题:Split a string into chunks of the same length然后我将在此仅重复相关部分。
您正在对Char
进行分区,但String
是UTF-16编码的,那么您可以在至少三种情况下生成已损坏的字符串:
"dž".Length > 1
。更多关于How can I perform a Unicode aware character by character comparison?上的这个和其他文化问题。一个提议的(和未经测试的)实现可能是这样的:
public static IEnumerable<string> Split(this string value, int desiredLength)
{
var characters = StringInfo.GetTextElementEnumerator(value);
while (characters.MoveNext())
yield return String.Concat(Take(characters, desiredLength));
}
private static IEnumerable<string> Take(TextElementEnumerator enumerator, int count)
{
for (int i = 0; i < count; ++i)
{
yield return (string)enumerator.Current;
if (!enumerator.MoveNext())
yield break;
}
}
它没有针对速度进行优化(正如您所看到的那样,我尝试使用枚举来保持代码简短和清晰)但是,对于大文件,它仍然比您的实现表现更好(请参阅下一段的原因)。
关于您的代码请注意:
ArrayList
(?!)来保存结果。另请注意,通过这种方式,您可以多次调整ArrayList
的大小(即使给定输入大小和块大小,也可以知道其最终大小)。strSegmentData
重建多次,如果你需要累积必须使用的字符StringBuilder
,否则每个操作都会分配一个新的字符串并复制旧的值(它很慢而且它也给垃圾收集器增加了压力) )。有更快的实施(请参阅链接的代码审核帖子,尤其是Heslacher's implementation以获得更快的版本),如果您不需要正确处理Unicode(您确实确定)只有US ASCII字符)然后还有一个漂亮的readable implementation from Jon Skeet(请注意,在分析代码之后,您仍然可以提高其预分配正确大小输出列表的大文件的性能)。我不在这里重复他们的代码,请参阅链接的帖子。
在您特定的中,您不需要读取内存中的整个大文件,您可以及时读取/解析 n 个字符(不要过于担心磁盘访问,I / O被缓冲)。它会略微降低性能,但会大大提高内存使用率。或者,您可以逐行阅读(管理处理跨行块)。
答案 1 :(得分:0)
以下是我对您的问题和代码的分析(阅读评论)
using (StreamReader srSegmentData = new StreamReader(fileNamePath))
{
string strSegmentData = "";
string line = srSegmentData.ReadToEnd(); // Why are you reading this till the end if it is such a long string?
int startPos = 0;
ArrayList alSegments = new ArrayList(); // Better choice would be to use List<string>
while (startPos < line.Length && (line.Length - startPos) >= segmentSize)
{
strSegmentData = strSegmentData + line.Substring(startPos, segmentSize) + Environment.NewLine; // Seem like you are inserting linebreaks at specified interval in your original string. Is that what you want?
alSegments.Add(line.Substring(startPos, segmentSize) + Environment.NewLine); // Why are you recalculating the Substring? Why are you appending the newline if the aim is to just "split"
startPos = startPos + segmentSize;
}
}
做出各种假设,下面是我建议用于拆分长字符串的代码。这只是一种干净的方式来做你在样本中做的事情。您可以对此进行优化,但不确定您的查找速度有多快。
static void Main(string[] args) {
string fileNamePath = "ConsoleApplication1.pdb";
var segmentSize = 32;
var op = ReadSplit(fileNamePath, segmentSize);
var joinedSTring = string.Join(Environment.NewLine, op);
}
static List<string> ReadSplit(string filePath, int segmentSize) {
var splitOutput = new List<string>();
using (var file = new StreamReader(filePath, Encoding.UTF8, true, 8 * 1024 )) {
char []buffer = new char[segmentSize];
while (!file.EndOfStream) {
int n = file.ReadBlock(buffer, 0, segmentSize);
splitOutput.Add(new string(buffer, 0, n));
}
}
return splitOutput;
}
我没有对我的版本进行任何性能测试,但我的猜测是它比你的版本更快。
此外,我不确定您打算如何使用输出,但在执行I / O时进行的优化是使用异步调用。处理大型string
时的良好优化(以可读性和复杂性为代价)是坚持char[]
请注意
StringReader
类而不是StreamReader
类