/// <summary> /// Reads next piece of string with spaces defined by <see cref="char.IsWhiteSpace(char)"/> is encountered as delimiters. The whitespaces will not be included in the returned string. /// </summary> /// <param name="reader">A <see cref="TextReader" /> object.</param> /// <returns>A <see cref="string"/> piece retrieved from the <paramref name="reader"/>.</returns> public static string ReadPiece(this TextReader reader) { int ci; while ((ci = reader.Read()) != -1 && ((char)ci).IsWhiteSpace()) { ; } if (ci == -1) { return(null); } else { var sb = StringBuilderCache.Acquire(); sb.Append((char)ci); while ((ci = reader.Read()) != -1 && !((char)ci).IsWhiteSpace()) { sb.Append((char)ci); } return(StringBuilderCache.GetStringAndRelease(sb)); } }
public virtual string ReadString() { ThrowIfDisposed(); int currPos = 0; int n; int stringLength; int readLength; int charsRead; // Length of the string in bytes, not chars stringLength = Read7BitEncodedInt(); if (stringLength < 0) { throw new IOException(SR.Format(SR.IO_InvalidStringLen_Len, stringLength)); } if (stringLength == 0) { return(string.Empty); } if (_charBytes == null) { _charBytes = new byte[MaxCharBytesSize]; } if (_charBuffer == null) { _charBuffer = new char[_maxCharsSize]; } StringBuilder?sb = null; do { readLength = ((stringLength - currPos) > MaxCharBytesSize) ? MaxCharBytesSize : (stringLength - currPos); n = _stream.Read(_charBytes, 0, readLength); if (n == 0) { throw Error.GetEndOfFile(); } charsRead = _decoder.GetChars(_charBytes, 0, n, _charBuffer, 0); if (currPos == 0 && n == stringLength) { return(new string(_charBuffer, 0, charsRead)); } if (sb == null) { sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller. } sb.Append(_charBuffer, 0, charsRead); currPos += n; } while (currPos < stringLength); return(StringBuilderCache.GetStringAndRelease(sb)); }
private static string GetRelativePath(string relativeTo, string path, StringComparison comparisonType) { if (string.IsNullOrEmpty(relativeTo)) { throw new ArgumentNullException(nameof(relativeTo)); } if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentNullException(nameof(path)); } Debug.Assert(comparisonType == StringComparison.Ordinal || comparisonType == StringComparison.OrdinalIgnoreCase); relativeTo = GetFullPath(relativeTo); path = GetFullPath(path); // Need to check if the roots are different- if they are we need to return the "to" path. if (!PathInternal.AreRootsEqual(relativeTo, path, comparisonType)) { return(path); } int commonLength = PathInternal.GetCommonPathLength(relativeTo, path, ignoreCase: comparisonType == StringComparison.OrdinalIgnoreCase); // If there is nothing in common they can't share the same root, return the "to" path as is. if (commonLength == 0) { return(path); } // Trailing separators aren't significant for comparison int relativeToLength = relativeTo.Length; if (PathInternal.EndsInDirectorySeparator(relativeTo)) { relativeToLength--; } bool pathEndsInSeparator = PathInternal.EndsInDirectorySeparator(path); int pathLength = path.Length; if (pathEndsInSeparator) { pathLength--; } // If we have effectively the same path, return "." if (relativeToLength == pathLength && commonLength >= relativeToLength) { return("."); } // We have the same root, we need to calculate the difference now using the // common Length and Segment count past the length. // // Some examples: // // C:\Foo C:\Bar L3, S1 -> ..\Bar // C:\Foo C:\Foo\Bar L6, S0 -> Bar // C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar // C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar StringBuilder sb = StringBuilderCache.Acquire(Math.Max(relativeTo.Length, path.Length)); // Add parent segments for segments past the common on the "from" path if (commonLength < relativeToLength) { sb.Append(PathInternal.ParentDirectoryPrefix); for (int i = commonLength; i < relativeToLength; i++) { if (PathInternal.IsDirectorySeparator(relativeTo[i])) { sb.Append(PathInternal.ParentDirectoryPrefix); } } } else if (PathInternal.IsDirectorySeparator(path[commonLength])) { // No parent segments and we need to eat the initial separator // (C:\Foo C:\Foo\Bar case) commonLength++; } // Now add the rest of the "to" path, adding back the trailing separator int count = pathLength - commonLength; if (pathEndsInSeparator) { count++; } sb.Append(path, commonLength, count); return(StringBuilderCache.GetStringAndRelease(sb)); }
private static string CombineNoChecks(string path1, string path2, string path3, string path4) { if (path1.Length == 0) { return(CombineNoChecks(path2, path3, path4)); } if (path2.Length == 0) { return(CombineNoChecks(path1, path3, path4)); } if (path3.Length == 0) { return(CombineNoChecks(path1, path2, path4)); } if (path4.Length == 0) { return(CombineNoChecks(path1, path2, path3)); } if (IsPathRooted(path4)) { return(path4); } if (IsPathRooted(path3)) { return(CombineNoChecks(path3, path4)); } if (IsPathRooted(path2)) { return(CombineNoChecks(path2, path3, path4)); } bool hasSep1 = PathInternal.IsDirectoryOrVolumeSeparator(path1[path1.Length - 1]); bool hasSep2 = PathInternal.IsDirectoryOrVolumeSeparator(path2[path2.Length - 1]); bool hasSep3 = PathInternal.IsDirectoryOrVolumeSeparator(path3[path3.Length - 1]); if (hasSep1 && hasSep2 && hasSep3) { // Use string.Concat overload that takes four strings return(path1 + path2 + path3 + path4); } else { // string.Concat only has string-based overloads up to four arguments; after that requires allocating // a params string[]. Instead, try to use a cached StringBuilder. StringBuilder sb = StringBuilderCache.Acquire(path1.Length + path2.Length + path3.Length + path4.Length + 3); sb.Append(path1); if (!hasSep1) { sb.Append(PathInternal.DirectorySeparatorChar); } sb.Append(path2); if (!hasSep2) { sb.Append(PathInternal.DirectorySeparatorChar); } sb.Append(path3); if (!hasSep3) { sb.Append(PathInternal.DirectorySeparatorChar); } sb.Append(path4); return(StringBuilderCache.GetStringAndRelease(sb)); } }
public static string Combine(params string[] paths) { if (paths == null) { throw new ArgumentNullException(nameof(paths)); } Contract.EndContractBlock(); int finalSize = 0; int firstComponent = 0; // We have two passes, the first calculates how large a buffer to allocate and does some precondition // checks on the paths passed in. The second actually does the combination. for (int i = 0; i < paths.Length; i++) { if (paths[i] == null) { throw new ArgumentNullException(nameof(paths)); } if (paths[i].Length == 0) { continue; } PathInternal.CheckInvalidPathChars(paths[i]); if (IsPathRooted(paths[i])) { firstComponent = i; finalSize = paths[i].Length; } else { finalSize += paths[i].Length; } char ch = paths[i][paths[i].Length - 1]; if (!PathInternal.IsDirectoryOrVolumeSeparator(ch)) { finalSize++; } } StringBuilder finalPath = StringBuilderCache.Acquire(finalSize); for (int i = firstComponent; i < paths.Length; i++) { if (paths[i].Length == 0) { continue; } if (finalPath.Length == 0) { finalPath.Append(paths[i]); } else { char ch = finalPath[finalPath.Length - 1]; if (!PathInternal.IsDirectoryOrVolumeSeparator(ch)) { finalPath.Append(PathInternal.DirectorySeparatorChar); } finalPath.Append(paths[i]); } } return(StringBuilderCache.GetStringAndRelease(finalPath)); }
public virtual String ReadString() { Contract.Ensures(Contract.Result <String>() != null); if (m_stream == null) { __Error.FileNotOpen(); } int currPos = 0; int n; int stringLength; int readLength; int charsRead; // Length of the string in bytes, not chars stringLength = Read7BitEncodedInt(); if (stringLength < 0) { throw new IOException(Environment.GetResourceString("IO.IO_InvalidStringLen_Len", stringLength)); } if (stringLength == 0) { return(String.Empty); } if (m_charBytes == null) { m_charBytes = new byte[MaxCharBytesSize]; } if (m_charBuffer == null) { m_charBuffer = new char[m_maxCharsSize]; } StringBuilder sb = null; do { readLength = ((stringLength - currPos) > MaxCharBytesSize)?MaxCharBytesSize:(stringLength - currPos); n = m_stream.Read(m_charBytes, 0, readLength); if (n == 0) { __Error.EndOfFile(); } charsRead = m_decoder.GetChars(m_charBytes, 0, n, m_charBuffer, 0); if (currPos == 0 && n == stringLength) { return(new String(m_charBuffer, 0, charsRead)); } if (sb == null) { sb = StringBuilderCache.Acquire(stringLength); // Actual string length in chars may be smaller. } sb.Append(m_charBuffer, 0, charsRead); currPos += n; } while (currPos < stringLength); return(StringBuilderCache.GetStringAndRelease(sb)); }
/// <summary> /// Normalize separators in the given path. Converts forward slashes into back slashes and compresses slash runs, keeping initial 2 if present. /// Also trims initial whitespace in front of "rooted" paths (see PathStartSkip). /// /// This effectively replicates the behavior of the legacy NormalizePath when it was called with fullCheck=false and expandShortpaths=false. /// The current NormalizePath gets directory separator normalization from Win32's GetFullPathName(), which will resolve relative paths and as /// such can't be used here (and is overkill for our uses). /// /// Like the current NormalizePath this will not try and analyze periods/spaces within directory segments. /// </summary> /// <remarks> /// The only callers that used to use Path.Normalize(fullCheck=false) were Path.GetDirectoryName() and Path.GetPathRoot(). Both usages do /// not need trimming of trailing whitespace here. /// /// GetPathRoot() could technically skip normalizing separators after the second segment- consider as a future optimization. /// /// For legacy desktop behavior with ExpandShortPaths: /// - It has no impact on GetPathRoot() so doesn't need consideration. /// - It could impact GetDirectoryName(), but only if the path isn't relative (C:\ or \\Server\Share). /// /// In the case of GetDirectoryName() the ExpandShortPaths behavior was undocumented and provided inconsistent results if the path was /// fixed/relative. For example: "C:\PROGRA~1\A.TXT" would return "C:\Program Files" while ".\PROGRA~1\A.TXT" would return ".\PROGRA~1". If you /// ultimately call GetFullPath() this doesn't matter, but if you don't or have any intermediate string handling could easily be tripped up by /// this undocumented behavior. /// </remarks> internal static string NormalizeDirectorySeparators(string path) { if (string.IsNullOrEmpty(path)) { return(path); } char current; int start = PathStartSkip(path); if (start == 0) { // Make a pass to see if we need to normalize so we can potentially skip allocating bool normalized = true; for (int i = 0; i < path.Length; i++) { current = path[i]; if (IsDirectorySeparator(current) && (current != Path.DirectorySeparatorChar // Check for sequential separators past the first position (we need to keep initial two for UNC/extended) || (i > 0 && i + 1 < path.Length && IsDirectorySeparator(path[i + 1])))) { normalized = false; break; } } if (normalized) { return(path); } } StringBuilder builder = StringBuilderCache.Acquire(path.Length); if (IsDirectorySeparator(path[start])) { start++; builder.Append(Path.DirectorySeparatorChar); } for (int i = start; i < path.Length; i++) { current = path[i]; // If we have a separator if (IsDirectorySeparator(current)) { // If the next is a separator, skip adding this if (i + 1 < path.Length && IsDirectorySeparator(path[i + 1])) { continue; } // Ensure it is the primary separator current = Path.DirectorySeparatorChar; } builder.Append(current); } return(StringBuilderCache.GetStringAndRelease(builder)); }
// We rely on Windows to remove relative segments on Windows. This would need to be updated to // handle the proper rooting on Windows if we for some reason need it. /// <summary> /// Try to remove relative segments from the given path (without combining with a root). /// </summary> /// <param name="skip">Skip the specified number of characters before evaluating.</param> internal static string RemoveRelativeSegments(string path, int skip = 0) { bool flippedSeparator = false; // Remove "//", "/./", and "/../" from the path by copying each character to the output, // except the ones we're removing, such that the builder contains the normalized path // at the end. var sb = StringBuilderCache.Acquire(path.Length); if (skip > 0) { sb.Append(path, 0, skip); } int componentCharCount = 0; for (int i = skip; i < path.Length; i++) { char c = path[i]; if (PathInternal.IsDirectorySeparator(c) && i + 1 < path.Length) { componentCharCount = 0; // Skip this character if it's a directory separator and if the next character is, too, // e.g. "parent//child" => "parent/child" if (PathInternal.IsDirectorySeparator(path[i + 1])) { continue; } // Skip this character and the next if it's referring to the current directory, // e.g. "parent/./child" =? "parent/child" if ((i + 2 == path.Length || PathInternal.IsDirectorySeparator(path[i + 2])) && path[i + 1] == '.') { i++; continue; } // Skip this character and the next two if it's referring to the parent directory, // e.g. "parent/child/../grandchild" => "parent/grandchild" if (i + 2 < path.Length && (i + 3 == path.Length || PathInternal.IsDirectorySeparator(path[i + 3])) && path[i + 1] == '.' && path[i + 2] == '.') { // Unwind back to the last slash (and if there isn't one, clear out everything). int s; for (s = sb.Length - 1; s >= 0; s--) { if (PathInternal.IsDirectorySeparator(sb[s])) { sb.Length = s; break; } } if (s < 0) { sb.Length = 0; } i += 2; continue; } } if (++componentCharCount > PathInternal.MaxComponentLength) { throw new PathTooLongException(); } // Normalize the directory separator if needed if (c != Path.DirectorySeparatorChar && c == Path.AltDirectorySeparatorChar) { c = Path.DirectorySeparatorChar; flippedSeparator = true; } sb.Append(c); } if (flippedSeparator || sb.Length != path.Length) { return(StringBuilderCache.GetStringAndRelease(sb)); } else { // We haven't changed the source path, return the original StringBuilderCache.Release(sb); return(path); } }
static string _innerReadToNewlineDelimiter(this TextReader reader, string keyword, bool returnIfKeywordNotFound) { var i = 0; var j = 0; int ci; var sb = StringBuilderCache.Acquire(); var delimiter = Environment.NewLine; bool secondHit = false; while ((ci = reader.Read()) != -1) { var c = (char)ci; if (c == delimiter[j]) { ++j; if (j == delimiter.Length) { if (secondHit) { break; } else { while ((ci = reader.Read()) != -1 && ci != delimiter[0] && ((char)ci).IsWhiteSpace()) { ; } if (ci == -1) { break; } else if (ci == delimiter[0]) { secondHit = true; j = 1; continue; } else { c = (char)ci; } } } } else if (c == delimiter.Last()) { while ((ci = reader.Read()) != -1 && ci != delimiter[0] && ((char)ci).IsWhiteSpace()) { ; } if (ci == -1) { break; } else if (ci == delimiter[0]) { secondHit = true; j = 1; continue; } else { c = (char)ci; } } else { if (c == delimiter[0]) { j = 1; } else { j = 0; } } if (c == keyword[i]) { ++i; if (i == keyword.Length) { return(StringBuilderCache.GetStringAndRelease(sb)); } } else { if (i != 0) { sb.Append(keyword.Substring(0, i)); i = 0; } if (c == keyword[0]) { ++i; } else { sb.Append(c); } } } if (returnIfKeywordNotFound) { return(StringBuilderCache.GetStringAndRelease(sb)); } else { StringBuilderCache.Release(sb); return(null); } }
public static string ReadTo(this TextReader reader, string keyword, string delimiter, bool returnIfKeywordNotFound = false) { if (delimiter == Environment.NewLine) { return(_innerReadToNewlineDelimiter(reader, keyword, returnIfKeywordNotFound)); } var i = 0; var j = 0; int ci; var sb = StringBuilderCache.Acquire(); while ((ci = reader.Read()) != -1) { var c = (char)ci; if (c == delimiter[j]) { ++j; if (j == delimiter.Length) { break; } } else { if (c == delimiter[0]) { j = 1; } else { j = 0; } } if (c == keyword[i]) { ++i; if (i == keyword.Length) { return(StringBuilderCache.GetStringAndRelease(sb)); } } else { if (i != 0) { sb.Append(keyword.Substring(0, i)); i = 0; } if (c == keyword[0]) { ++i; } else { sb.Append(c); } } } if (returnIfKeywordNotFound) { return(StringBuilderCache.GetStringAndRelease(sb)); } else { StringBuilderCache.Release(sb); return(null); } }